Expose almost all World Lua bindings
This commit is contained in:
parent
be6f637d9b
commit
7f16e643f3
29
ADDITIONS.md
29
ADDITIONS.md
@ -137,6 +137,27 @@ val color: TileColor = TileColor.DEFAULT
|
|||||||
* Added `world.unsetUniverseFlag(flag: String): Boolean`
|
* Added `world.unsetUniverseFlag(flag: String): Boolean`
|
||||||
* Added `world.placeDungeonAsync(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): RpcPromise<Boolean>`
|
* Added `world.placeDungeonAsync(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): RpcPromise<Boolean>`
|
||||||
* Added `world.tryPlaceDungeonAsync(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): RpcPromise<Boolean>`
|
* Added `world.tryPlaceDungeonAsync(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): RpcPromise<Boolean>`
|
||||||
|
* Added `world.materialColorName(pos: Vector2i, layer: String): String`. Returns one of next strings:
|
||||||
|
* `"default"`
|
||||||
|
* `"red"`
|
||||||
|
* `"blue"`
|
||||||
|
* `"green"`
|
||||||
|
* `"yellow"`
|
||||||
|
* `"brown"`
|
||||||
|
* `"purple"`
|
||||||
|
* `"black"`
|
||||||
|
* `"white"`
|
||||||
|
* Added `world.spawnLiquidPromise(pos: Vector2i, liquid: Any, quantity: Float): RpcPromise<Boolean>`
|
||||||
|
* However, correct clientside result will be returned _only_ when using native protocol
|
||||||
|
* Added `world.damageTilesPromise(positions: List<Vector2i>, layer: String, damageSource: Vector2d, damageType: String, damageAmount: Double, harvestLevel: Int = 999, sourceEntity: EntityID = 0): RpcPromise<String>`, which return one of next values on promise completion:
|
||||||
|
* `"none"`
|
||||||
|
* `"protected"`
|
||||||
|
* `"normal"`
|
||||||
|
* Keep in mind that it returns top-most damage status, which means:
|
||||||
|
* If some tiles were protected, and others were not, it will return `"normal"`.
|
||||||
|
* If all tiles were protected, it will return `"protected"`.
|
||||||
|
* If none tiles were damaged, it will return `"none"`.
|
||||||
|
* Added `world.damageTileAreaPromise(radius: Double, position: Vector2i, layer: String, damageSource: Vector2d, damageType: String, damageAmount: Double, harvestLevel: Int = 999, sourceEntity: EntityID = 0): RpcPromise<String>`, with same notes as `world.damageTilesPromise()` apply
|
||||||
|
|
||||||
#### Changes
|
#### Changes
|
||||||
|
|
||||||
@ -155,6 +176,14 @@ val color: TileColor = TileColor.DEFAULT
|
|||||||
* `world.tryPlaceDungeon(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): Boolean` now accepts optional `seed`. If not specified, engine will determine one (like original engine does).
|
* `world.tryPlaceDungeon(name: String, position: Vector2d, dungeonID: Int?, seed: Long?): Boolean` now accepts optional `seed`. If not specified, engine will determine one (like original engine does).
|
||||||
* Please update code to use `world.tryPlaceDungeonAsync`, because there are absolutely no guarantees dungeon will be generated the moment `world.tryPlaceDungeon` call returns
|
* Please update code to use `world.tryPlaceDungeonAsync`, because there are absolutely no guarantees dungeon will be generated the moment `world.tryPlaceDungeon` call returns
|
||||||
* `world.setDungeonGravity(id: Int, gravity: Either<Double, Vector2d>)` now accept directional vector. **Attention:** Directional gravity is WIP.
|
* `world.setDungeonGravity(id: Int, gravity: Either<Double, Vector2d>)` now accept directional vector. **Attention:** Directional gravity is WIP.
|
||||||
|
* `world.setMaterialColor(pos: Vector2i, layer: String, color: Any): Boolean`
|
||||||
|
* Now returns boolean whenever cell was set (returns `false` when called with position outside of loaded chunks)
|
||||||
|
* Now accepts string name along integer index
|
||||||
|
* `world.placeMaterial(pos: Vector2i, layer: String, material: String, hueShift: Number?, allowOverlap: Boolean): RpcPromise<Vector2i>` now returns `RpcPromise<Vector2i>` of unapplied tile modifications instead of `Boolean`, which wasn't representative anyway, and outright wrong if this function was called on client
|
||||||
|
* However, correct clientside results will be returned _only_ when using native protocol
|
||||||
|
* `world.placeMod(pos: Vector2i, layer: String, mod: String, hueShift: Number?, allowOverlap: Boolean): RpcPromise<Vector2i>` now returns `RpcPromise<Vector2i>` of unapplied tile modifications instead of `Boolean`, which wasn't representative anyway, and outright wrong if this function was called on client
|
||||||
|
* However, correct clientside results will be returned _only_ when using native protocol
|
||||||
|
* `world.spawnLiquid(pos: Vector2i, liquid: Any, quantity: Float): Boolean` now accepts both liquid ID and liquid name. However, you should be using `world.spawnLiquidPromise(pos: Vector2i, liquid: Any, quantity: Float): RpcPromise<Boolean>` instead
|
||||||
|
|
||||||
#### Fixes
|
#### Fixes
|
||||||
|
|
||||||
|
@ -47,44 +47,7 @@ operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, v
|
|||||||
|
|
||||||
operator fun <K : Any, V : Any> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> = put(key, value)
|
operator fun <K : Any, V : Any> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> = put(key, value)
|
||||||
|
|
||||||
fun String.sintern(): String = Starbound.STRINGS.intern(this)
|
|
||||||
|
|
||||||
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
||||||
inline fun <reified T> Gson.fromJson(reader: JsonElement): T? = getAdapter(T::class.java).read(FastJsonTreeReader(reader))
|
inline fun <reified T> Gson.fromJson(reader: JsonElement): T? = getAdapter(T::class.java).read(FastJsonTreeReader(reader))
|
||||||
|
|
||||||
fun <T> Gson.fromJsonFast(reader: JsonElement, type: Class<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
fun <T> Gson.fromJsonFast(reader: JsonElement, type: Class<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
||||||
|
|
||||||
/**
|
|
||||||
* guarantees even distribution of tasks while also preserving encountered order of elements
|
|
||||||
*/
|
|
||||||
fun <T> Collection<IStarboundFile>.batch(executor: ForkJoinPool, batchSize: Int = 16, mapper: (IStarboundFile) -> KOptional<T>): Stream<T> {
|
|
||||||
require(batchSize >= 1) { "Invalid batch size: $batchSize" }
|
|
||||||
|
|
||||||
if (batchSize == 1 || size <= batchSize) {
|
|
||||||
val tasks = ArrayList<ForkJoinTask<KOptional<T>>>()
|
|
||||||
|
|
||||||
for (listedFile in this) {
|
|
||||||
tasks.add(executor.submit(Callable { mapper.invoke(listedFile) }))
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks.stream().map { it.join() }.filter { it.isPresent }.map { it.value }
|
|
||||||
}
|
|
||||||
|
|
||||||
val batches = ArrayList<ForkJoinTask<List<KOptional<T>>>>()
|
|
||||||
var batch = ArrayList<IStarboundFile>(batchSize)
|
|
||||||
|
|
||||||
for (listedFile in this) {
|
|
||||||
batch.add(listedFile)
|
|
||||||
|
|
||||||
if (batch.size >= batchSize) {
|
|
||||||
val mbatch = batch
|
|
||||||
batches.add(executor.submit(Callable { mbatch.map { mapper.invoke(it) } }))
|
|
||||||
batch = ArrayList(batchSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (batch.isNotEmpty())
|
|
||||||
batches.add(executor.submit(Callable { batch.map { mapper.invoke(it) } }))
|
|
||||||
|
|
||||||
return batches.stream().flatMap { it.join().stream() }.filter { it.isPresent }.map { it.value }
|
|
||||||
}
|
|
||||||
|
@ -24,6 +24,8 @@ import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
|||||||
import ru.dbotthepony.kstarbound.client.render.Mesh
|
import ru.dbotthepony.kstarbound.client.render.Mesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamageResult
|
||||||
import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
@ -43,6 +45,7 @@ import ru.dbotthepony.kstarbound.world.World
|
|||||||
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
import ru.dbotthepony.kstarbound.world.api.ITileAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileView
|
import ru.dbotthepony.kstarbound.world.api.TileView
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||||
import ru.dbotthepony.kstarbound.world.positiveModulo
|
import ru.dbotthepony.kstarbound.world.positiveModulo
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -332,7 +335,7 @@ class ClientWorld(
|
|||||||
modifications: Collection<Pair<Vector2i, TileModification>>,
|
modifications: Collection<Pair<Vector2i, TileModification>>,
|
||||||
allowEntityOverlap: Boolean,
|
allowEntityOverlap: Boolean,
|
||||||
ignoreTileProtection: Boolean
|
ignoreTileProtection: Boolean
|
||||||
): List<Pair<Vector2i, TileModification>> {
|
): CompletableFuture<List<Pair<Vector2i, TileModification>>> {
|
||||||
// send packets to server here
|
// send packets to server here
|
||||||
// this is required because Lua scripts call this method
|
// this is required because Lua scripts call this method
|
||||||
// and Lua scripts want these changes to be applied serverside (good game security, i approve)
|
// and Lua scripts want these changes to be applied serverside (good game security, i approve)
|
||||||
@ -400,6 +403,16 @@ class ClientWorld(
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun damageTiles(
|
||||||
|
positions: Collection<IStruct2i>,
|
||||||
|
isBackground: Boolean,
|
||||||
|
sourcePosition: Vector2d,
|
||||||
|
damage: TileDamage,
|
||||||
|
source: AbstractEntity?
|
||||||
|
): CompletableFuture<TileDamageResult> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val ring = listOf(
|
val ring = listOf(
|
||||||
Vector2i(0, 0),
|
Vector2i(0, 0),
|
||||||
|
@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.defs.dungeon
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonArray
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
@ -29,7 +28,6 @@ import ru.dbotthepony.kstarbound.json.builder.JsonIgnore
|
|||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.ImmutableTileState
|
import ru.dbotthepony.kstarbound.world.api.ImmutableTileState
|
||||||
import ru.dbotthepony.kstarbound.world.api.MutableLiquidState
|
import ru.dbotthepony.kstarbound.world.api.MutableLiquidState
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
@ -114,14 +112,14 @@ abstract class DungeonBrush {
|
|||||||
isBackground,
|
isBackground,
|
||||||
data.material, data.modifier,
|
data.material, data.modifier,
|
||||||
data.modHueShift, data.hueShift,
|
data.modHueShift, data.hueShift,
|
||||||
TileColor.entries.firstOrNull { it.lowercase == data.colorVariant.lowercase() } ?: TileColor.entries[data.colorVariant.toIntOrNull() ?: throw JsonSyntaxException("Invalid color variant: ${data.colorVariant}")]
|
TileColor.entries.firstOrNull { it.jsonName == data.colorVariant.lowercase() } ?: TileColor.entries[data.colorVariant.toIntOrNull() ?: throw JsonSyntaxException("Invalid color variant: ${data.colorVariant}")]
|
||||||
)
|
)
|
||||||
|
|
||||||
constructor(isBackground: Boolean, material: Registry.Ref<TileDefinition>, data: TiledData) : this(
|
constructor(isBackground: Boolean, material: Registry.Ref<TileDefinition>, data: TiledData) : this(
|
||||||
isBackground, material,
|
isBackground, material,
|
||||||
data.modifier, data.hueshift,
|
data.modifier, data.hueshift,
|
||||||
data.modhueshift,
|
data.modhueshift,
|
||||||
TileColor.entries.firstOrNull { it.lowercase == data.colorVariant.lowercase() } ?: TileColor.entries[data.colorVariant.toIntOrNull() ?: throw JsonSyntaxException("Invalid color variant: ${data.colorVariant}")]
|
TileColor.entries.firstOrNull { it.jsonName == data.colorVariant.lowercase() } ?: TileColor.entries[data.colorVariant.toIntOrNull() ?: throw JsonSyntaxException("Invalid color variant: ${data.colorVariant}")]
|
||||||
)
|
)
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.tile
|
package ru.dbotthepony.kstarbound.defs.tile
|
||||||
|
|
||||||
enum class TileDamageResult {
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
|
|
||||||
|
enum class TileDamageResult : IStringSerializable {
|
||||||
NONE,
|
NONE,
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
NORMAL;
|
NORMAL;
|
||||||
|
|
||||||
|
override val jsonName: String = name.lowercase()
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class WorldTemplate(val geometry: WorldGeometry) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun surfaceLevel(): Int {
|
val surfaceLevel: Int get() {
|
||||||
val parameters = worldParameters
|
val parameters = worldParameters
|
||||||
|
|
||||||
if (parameters is TerrestrialWorldParameters) {
|
if (parameters is TerrestrialWorldParameters) {
|
||||||
@ -169,6 +169,18 @@ class WorldTemplate(val geometry: WorldGeometry) {
|
|||||||
return geometry.size.y / 2
|
return geometry.size.y / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val undergroundLevel: Int get() {
|
||||||
|
val parameters = worldParameters
|
||||||
|
|
||||||
|
if (parameters is TerrestrialWorldParameters) {
|
||||||
|
return parameters.surfaceLayer.layerMinHeight
|
||||||
|
} else if (parameters is FloatingDungeonWorldParameters) {
|
||||||
|
return parameters.dungeonUndergroundLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
class PotentialBiomeItems(
|
class PotentialBiomeItems(
|
||||||
// Potential items that would spawn at the given block assuming it is at
|
// Potential items that would spawn at the given block assuming it is at
|
||||||
val surfaceBiomeItems: List<BiomePlaceables.Placement> = listOf(),
|
val surfaceBiomeItems: List<BiomePlaceables.Placement> = listOf(),
|
||||||
|
@ -359,16 +359,6 @@ fun TableFactory.from(value: Poly?): Table? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TableFactory.fromCollection(value: Collection<JsonElement?>): Table {
|
|
||||||
val table = newTable(value.size, 0)
|
|
||||||
|
|
||||||
for ((k, v) in value.withIndex()) {
|
|
||||||
table.rawset(Conversions.normaliseKey(k + 1), from(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: IStruct2i?): Table? {
|
fun TableFactory.from(value: IStruct2i?): Table? {
|
||||||
value ?: return null
|
value ?: return null
|
||||||
|
|
||||||
@ -428,3 +418,12 @@ fun TableFactory.from(value: AABB?): Table? {
|
|||||||
it.rawset(4L, value.maxs.y)
|
it.rawset(4L, value.maxs.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TableFactory.tableFrom(collection: Collection<Any?>): Table {
|
||||||
|
val alloc = newTable(collection.size, 0)
|
||||||
|
|
||||||
|
for ((i, v) in collection.withIndex())
|
||||||
|
alloc[i + 1L] = v
|
||||||
|
|
||||||
|
return alloc
|
||||||
|
}
|
||||||
|
@ -40,6 +40,7 @@ import ru.dbotthepony.kstarbound.lua.toPoly
|
|||||||
import ru.dbotthepony.kstarbound.lua.toVector2d
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
||||||
import ru.dbotthepony.kstarbound.lua.toVector2i
|
import ru.dbotthepony.kstarbound.lua.toVector2i
|
||||||
import ru.dbotthepony.kstarbound.lua.unpackAsArray
|
import ru.dbotthepony.kstarbound.lua.unpackAsArray
|
||||||
|
import ru.dbotthepony.kstarbound.lua.userdata.LuaFuture
|
||||||
import ru.dbotthepony.kstarbound.math.AABB
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.Line2d
|
import ru.dbotthepony.kstarbound.math.Line2d
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||||
@ -518,9 +519,21 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
returnBuffer.setTo(from(self.gravityAt(toVector2d(pos))))
|
returnBuffer.setTo(from(self.gravityAt(toVector2d(pos))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callbacks["spawnLiquidPromise"] = luaFunction { pos: Table, liquid: Any, quantity: Number ->
|
||||||
|
val action = TileModification.Pour(if (liquid is ByteString) Registries.liquid.ref(liquid.decode()) else Registries.liquid.ref((liquid as Number).toInt()), quantity.toFloat())
|
||||||
|
|
||||||
|
returnBuffer.setTo(
|
||||||
|
LuaFuture(
|
||||||
|
future = self.applyTileModifications(listOf(toVector2i(pos) to action), false)
|
||||||
|
.thenApply { it.isEmpty() },
|
||||||
|
isLocal = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
callbacks["spawnLiquid"] = luaFunction { pos: Table, liquid: Any, quantity: Number ->
|
callbacks["spawnLiquid"] = luaFunction { pos: Table, liquid: Any, quantity: Number ->
|
||||||
val action = TileModification.Pour(if (liquid is ByteString) Registries.liquid.ref(liquid.decode()) else Registries.liquid.ref((liquid as Number).toInt()), quantity.toFloat())
|
val action = TileModification.Pour(if (liquid is ByteString) Registries.liquid.ref(liquid.decode()) else Registries.liquid.ref((liquid as Number).toInt()), quantity.toFloat())
|
||||||
returnBuffer.setTo(self.applyTileModifications(listOf(toVector2i(pos) to action), false).isEmpty())
|
returnBuffer.setTo(self.applyTileModifications(listOf(toVector2i(pos) to action), false).thenApply { it.isEmpty() }.getNow(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["destroyLiquid"] = luaFunction { pos: Table ->
|
callbacks["destroyLiquid"] = luaFunction { pos: Table ->
|
||||||
@ -549,7 +562,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
callbacks["type"] = luaFunction { returnBuffer.setTo(self.template.worldParameters?.typeName ?: "unknown") }
|
callbacks["type"] = luaFunction { returnBuffer.setTo(self.template.worldParameters?.typeName ?: "unknown") }
|
||||||
callbacks["size"] = luaFunction { returnBuffer.setTo(from(self.geometry.size)) }
|
callbacks["size"] = luaFunction { returnBuffer.setTo(from(self.geometry.size)) }
|
||||||
callbacks["inSurfaceLayer"] = luaFunction { pos: Table -> returnBuffer.setTo(self.template.isSurfaceLayer(toVector2i(pos))) }
|
callbacks["inSurfaceLayer"] = luaFunction { pos: Table -> returnBuffer.setTo(self.template.isSurfaceLayer(toVector2i(pos))) }
|
||||||
callbacks["surfaceLevel"] = luaFunction { returnBuffer.setTo(self.template.surfaceLevel()) }
|
callbacks["surfaceLevel"] = luaFunction { returnBuffer.setTo(self.template.surfaceLevel) }
|
||||||
callbacks["terrestrial"] = luaFunction { returnBuffer.setTo(self.template.worldParameters is TerrestrialWorldParameters) }
|
callbacks["terrestrial"] = luaFunction { returnBuffer.setTo(self.template.worldParameters is TerrestrialWorldParameters) }
|
||||||
|
|
||||||
callbacks["itemDropItem"] = luaFunction { id: Number ->
|
callbacks["itemDropItem"] = luaFunction { id: Number ->
|
||||||
@ -589,6 +602,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
callbacks["dungeonId"] = luaFunction { pos: Table -> returnBuffer.setTo(self.getCell(toVector2i(pos)).dungeonId) }
|
callbacks["dungeonId"] = luaFunction { pos: Table -> returnBuffer.setTo(self.getCell(toVector2i(pos)).dungeonId) }
|
||||||
|
|
||||||
provideWorldEntitiesBindings(self, callbacks, lua)
|
provideWorldEntitiesBindings(self, callbacks, lua)
|
||||||
|
provideWorldEnvironmentalBindings(self, callbacks, lua)
|
||||||
|
|
||||||
if (self is ServerWorld) {
|
if (self is ServerWorld) {
|
||||||
provideServerWorldBindings(self, callbacks, lua)
|
provideServerWorldBindings(self, callbacks, lua)
|
||||||
|
@ -0,0 +1,223 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import org.classdump.luna.ByteString
|
||||||
|
import org.classdump.luna.LuaRuntimeException
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import org.classdump.luna.lib.ArgumentIterator
|
||||||
|
import org.classdump.luna.runtime.ExecutionContext
|
||||||
|
import ru.dbotthepony.kommons.collect.map
|
||||||
|
import ru.dbotthepony.kommons.collect.toList
|
||||||
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamageResult
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamageType
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.isEmptyModifier
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.isEmptyTile
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyModifier
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.isNullTile
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.iterator
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunctionN
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaStub
|
||||||
|
import ru.dbotthepony.kstarbound.lua.nextOptionalInteger
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.tableFrom
|
||||||
|
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2i
|
||||||
|
import ru.dbotthepony.kstarbound.lua.userdata.LuaFuture
|
||||||
|
import ru.dbotthepony.kstarbound.util.valueOf
|
||||||
|
import ru.dbotthepony.kstarbound.world.TileModification
|
||||||
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
|
import ru.dbotthepony.kstarbound.world.tileAreaBrush
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
private val foregroundStr = ByteString.of("foreground")
|
||||||
|
private val backgroundStr = ByteString.of("background")
|
||||||
|
|
||||||
|
private fun isBackground(layer: ByteString): Boolean {
|
||||||
|
return if (layer == backgroundStr)
|
||||||
|
true
|
||||||
|
else if (layer == foregroundStr)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
throw LuaRuntimeException("Invalid tile layer $layer")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ExecutionContext.damageTilesImpl(self: World<*, *>, it: ArgumentIterator): CompletableFuture<TileDamageResult> {
|
||||||
|
val positions = it.nextTable().iterator().map { toVector2i(it.value) }.toList()
|
||||||
|
val isBackground = isBackground(it.nextString())
|
||||||
|
|
||||||
|
val sourcePosition = toVector2d(it.nextTable())
|
||||||
|
val damageType = TileDamageType.entries.valueOf(it.nextString().decode())
|
||||||
|
val damage = it.nextFloat()
|
||||||
|
val harvestLevel = it.nextOptionalInteger()?.toInt() ?: 999
|
||||||
|
val sourceEntity = self.entities[it.nextOptionalInteger()?.toInt() ?: 0]
|
||||||
|
|
||||||
|
return self.damageTiles(positions, isBackground, sourcePosition, TileDamage(damageType, damage, harvestLevel), sourceEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ExecutionContext.damageTileAreaImpl(self: World<*, *>, it: ArgumentIterator): CompletableFuture<TileDamageResult> {
|
||||||
|
val center = toVector2i(it.nextTable())
|
||||||
|
val radius = it.nextFloat()
|
||||||
|
val isBackground = isBackground(it.nextString())
|
||||||
|
|
||||||
|
val sourcePosition = toVector2d(it.nextTable())
|
||||||
|
val damageType = TileDamageType.entries.valueOf(it.nextString().decode())
|
||||||
|
val damage = it.nextFloat()
|
||||||
|
val harvestLevel = it.nextOptionalInteger()?.toInt() ?: 999
|
||||||
|
val sourceEntity = self.entities[it.nextOptionalInteger()?.toInt() ?: 0]
|
||||||
|
|
||||||
|
return self.damageTiles(tileAreaBrush(center, radius), isBackground, sourcePosition, TileDamage(damageType, damage, harvestLevel), sourceEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun provideWorldEnvironmentalBindings(self: World<*, *>, callbacks: Table, lua: LuaEnvironment) {
|
||||||
|
callbacks["lightLevel"] = luaStub("lightLevel")
|
||||||
|
callbacks["windLevel"] = luaStub("windLevel")
|
||||||
|
|
||||||
|
callbacks["breathable"] = luaFunction { pos: Table ->
|
||||||
|
returnBuffer.setTo(self.isBreathable(toVector2i(pos)))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["underground"] = luaFunction { pos: Table ->
|
||||||
|
returnBuffer.setTo(self.template.undergroundLevel >= toVector2d(pos).y)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["material"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
|
||||||
|
if (tile.material.isNullTile) {
|
||||||
|
returnBuffer.setTo()
|
||||||
|
} else if (tile.material.isEmptyTile) {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(tile.material.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
callbacks["mod"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
|
||||||
|
if (tile.modifier.isNotEmptyModifier) {
|
||||||
|
returnBuffer.setTo(tile.modifier.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["materialHueShift"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
returnBuffer.setTo(tile.hueShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["modHueShift"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
returnBuffer.setTo(tile.modifierHueShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["materialColor"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
returnBuffer.setTo(tile.color.ordinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["materialColorName"] = luaFunction { pos: Table, layer: ByteString ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
val tile = self.getCell(toVector2i(pos)).tile(isBackground)
|
||||||
|
returnBuffer.setTo(tile.color.jsonName)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setMaterialColor"] = luaFunction { pos: Table, layer: ByteString, color: Any ->
|
||||||
|
val isBackground = isBackground(layer)
|
||||||
|
|
||||||
|
val actualColor = if (color is Number)
|
||||||
|
TileColor.entries[color.toInt()]
|
||||||
|
else if (color is ByteString)
|
||||||
|
TileColor.entries.valueOf(color.decode())
|
||||||
|
else
|
||||||
|
throw LuaRuntimeException("Unknown tile color $color")
|
||||||
|
|
||||||
|
val actualPos = toVector2i(pos)
|
||||||
|
val cell = self.getCell(actualPos).mutable()
|
||||||
|
cell.tile(isBackground).color = actualColor
|
||||||
|
returnBuffer.setTo(self.setCell(actualPos, cell))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["oceanLevel"] = luaFunction { pos: Table ->
|
||||||
|
returnBuffer.setTo(self.template.cellInfo(toVector2i(pos)).oceanLiquidLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["environmentStatusEffects"] = luaFunction { pos: Table ->
|
||||||
|
returnBuffer.setTo(tableFrom(self.environmentStatusEffects(toVector2i(pos))))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["damageTiles"] = luaFunctionN("damageTiles") {
|
||||||
|
returnBuffer.setTo(damageTilesImpl(self, it).getNow(TileDamageResult.NONE) != TileDamageResult.NONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["damageTilesPromise"] = luaFunctionN("damageTilesPromise") {
|
||||||
|
returnBuffer.setTo(
|
||||||
|
LuaFuture(
|
||||||
|
future = damageTilesImpl(self, it).thenApply { it.jsonName },
|
||||||
|
isLocal = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["damageTileArea"] = luaFunctionN("damageTileArea") {
|
||||||
|
returnBuffer.setTo(damageTileAreaImpl(self, it).getNow(TileDamageResult.NONE) != TileDamageResult.NONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["damageTileAreaPromise"] = luaFunctionN("damageTileAreaPromise") {
|
||||||
|
returnBuffer.setTo(
|
||||||
|
LuaFuture(
|
||||||
|
future = damageTileAreaImpl(self, it).thenApply { it.jsonName },
|
||||||
|
isLocal = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["placeMaterial"] = luaFunctionN("placeMaterial") {
|
||||||
|
val pos = toVector2i(it.nextTable())
|
||||||
|
val isBackground = isBackground(it.nextString())
|
||||||
|
val material = Registries.tiles.getOrThrow(it.nextString().decode())
|
||||||
|
val hueShift: Float? = (it.nextAny() as? Number)?.let { it.toFloat() / 255f * 360f }
|
||||||
|
val allowOverlap = it.nextBoolean()
|
||||||
|
|
||||||
|
val action = TileModification.PlaceMaterial(isBackground, material.ref, hueShift)
|
||||||
|
|
||||||
|
// original engine return here doesn't make any sense whatsoever
|
||||||
|
returnBuffer.setTo(
|
||||||
|
LuaFuture(
|
||||||
|
future = self.applyTileModifications(listOf(pos to action), allowOverlap, false)
|
||||||
|
.thenApply { tableFrom(it.map { from(it.first) }) },
|
||||||
|
isLocal = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["placeMod"] = luaFunctionN("placeMod") {
|
||||||
|
val pos = toVector2i(it.nextTable())
|
||||||
|
val isBackground = isBackground(it.nextString())
|
||||||
|
val modifier = Registries.tileModifiers.getOrThrow(it.nextString().decode())
|
||||||
|
val hueShift: Float? = (it.nextAny() as? Number)?.let { it.toFloat() / 255f * 360f }
|
||||||
|
val allowOverlap = it.nextBoolean()
|
||||||
|
|
||||||
|
val action = TileModification.PlaceModifier(isBackground, modifier.ref, hueShift)
|
||||||
|
|
||||||
|
// original engine return here doesn't make any sense whatsoever
|
||||||
|
returnBuffer.setTo(
|
||||||
|
LuaFuture(
|
||||||
|
future = self.applyTileModifications(listOf(pos to action), allowOverlap, false)
|
||||||
|
.thenApply { tableFrom(it.map { from(it.first) }) },
|
||||||
|
isLocal = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -337,6 +337,18 @@ class ServerWorld private constructor(
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun damageTiles(
|
||||||
|
positions: Collection<IStruct2i>,
|
||||||
|
isBackground: Boolean,
|
||||||
|
sourcePosition: Vector2d,
|
||||||
|
damage: TileDamage,
|
||||||
|
source: AbstractEntity?
|
||||||
|
): CompletableFuture<TileDamageResult> {
|
||||||
|
return runBlocking {
|
||||||
|
CompletableFuture.completedFuture(damageTiles(positions, isBackground, sourcePosition, damage, source, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this method does not block if pacer is null (safe to use with runBlocking {})
|
* this method does not block if pacer is null (safe to use with runBlocking {})
|
||||||
*/
|
*/
|
||||||
@ -460,8 +472,10 @@ class ServerWorld private constructor(
|
|||||||
return topMost
|
return topMost
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean): List<Pair<Vector2i, TileModification>> {
|
override fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean): CompletableFuture<List<Pair<Vector2i, TileModification>>> {
|
||||||
return runBlocking { applyTileModifications(modifications, allowEntityOverlap, ignoreTileProtection, null) }
|
return runBlocking {
|
||||||
|
CompletableFuture.completedFuture(applyTileModifications(modifications, allowEntityOverlap, ignoreTileProtection, null))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean = false, pacer: ActionPacer?): List<Pair<Vector2i, TileModification>> {
|
suspend fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean = false, pacer: ActionPacer?): List<Pair<Vector2i, TileModification>> {
|
||||||
@ -629,7 +643,7 @@ class ServerWorld private constructor(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
LOGGER.debug("Trying to find player spawn position...")
|
LOGGER.debug("Trying to find player spawn position...")
|
||||||
var pos = hint ?: CompletableFuture.supplyAsync(Supplier { template.findSensiblePlayerStart(random) }, Starbound.EXECUTOR).await() ?: Vector2d(0.0, template.surfaceLevel().toDouble())
|
var pos = hint ?: CompletableFuture.supplyAsync(Supplier { template.findSensiblePlayerStart(random) }, Starbound.EXECUTOR).await() ?: Vector2d(0.0, template.surfaceLevel.toDouble())
|
||||||
var previous = pos
|
var previous = pos
|
||||||
LOGGER.debug("Trying to find player spawn position near {}...", pos)
|
LOGGER.debug("Trying to find player spawn position near {}...", pos)
|
||||||
|
|
||||||
@ -686,7 +700,7 @@ class ServerWorld private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = CompletableFuture.supplyAsync(Supplier { template.findSensiblePlayerStart(random) }, Starbound.EXECUTOR).await() ?: Vector2d(0.0, template.surfaceLevel().toDouble())
|
pos = CompletableFuture.supplyAsync(Supplier { template.findSensiblePlayerStart(random) }, Starbound.EXECUTOR).await() ?: Vector2d(0.0, template.surfaceLevel.toDouble())
|
||||||
|
|
||||||
if (previous != pos) {
|
if (previous != pos) {
|
||||||
LOGGER.debug("Still trying to find player spawn position near {}...", pos)
|
LOGGER.debug("Still trying to find player spawn position near {}...", pos)
|
||||||
|
@ -99,7 +99,7 @@ fun ICellAccess.castRay(
|
|||||||
val hitTiles = ArrayList<RayCastResult.HitCell>()
|
val hitTiles = ArrayList<RayCastResult.HitCell>()
|
||||||
var cellPosX = roundTowardsNegativeInfinity(start.x)
|
var cellPosX = roundTowardsNegativeInfinity(start.x)
|
||||||
var cellPosY = roundTowardsNegativeInfinity(start.y)
|
var cellPosY = roundTowardsNegativeInfinity(start.y)
|
||||||
var cell = getCell(cellPosX, cellPosY) ?: return RayCastResult(start, Vector2d.ZERO)
|
var cell = getCell(cellPosX, cellPosY)
|
||||||
|
|
||||||
val direction = (end - start).unitVector
|
val direction = (end - start).unitVector
|
||||||
|
|
||||||
|
28
src/main/kotlin/ru/dbotthepony/kstarbound/world/Utils.kt
Normal file
28
src/main/kotlin/ru/dbotthepony/kstarbound/world/Utils.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.world
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
fun tileAreaBrush(pos: Vector2i, radius: Double, square: Boolean = false): List<Vector2i> {
|
||||||
|
if (radius <= 0.0)
|
||||||
|
return emptyList()
|
||||||
|
|
||||||
|
val result = ObjectArrayList<Vector2i>()
|
||||||
|
val squareRadius = (if (square) Double.POSITIVE_INFINITY else radius).pow(2.0)
|
||||||
|
val intRadius = radius.roundToInt()
|
||||||
|
|
||||||
|
for (x in -intRadius .. intRadius) {
|
||||||
|
for (y in -intRadius .. intRadius) {
|
||||||
|
val length = x * x + y * y
|
||||||
|
|
||||||
|
if (length <= squareRadius) {
|
||||||
|
result.add(Vector2i(x + pos.x, y + pos.y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.sortWith { o1, o2 -> o1.distanceSquared(pos).compareTo(o2.distanceSquared(pos)) }
|
||||||
|
return result
|
||||||
|
}
|
@ -30,6 +30,8 @@ import ru.dbotthepony.kstarbound.Starbound
|
|||||||
import ru.dbotthepony.kstarbound.defs.tile.ARTIFICIAL_DUNGEON_ID
|
import ru.dbotthepony.kstarbound.defs.tile.ARTIFICIAL_DUNGEON_ID
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.DESTROYED_DUNGEON_ID
|
import ru.dbotthepony.kstarbound.defs.tile.DESTROYED_DUNGEON_ID
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
||||||
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamageResult
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
||||||
import ru.dbotthepony.kstarbound.defs.world.WorldStructure
|
import ru.dbotthepony.kstarbound.defs.world.WorldStructure
|
||||||
import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
||||||
@ -42,6 +44,7 @@ import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
|||||||
import ru.dbotthepony.kstarbound.network.packets.EntityMessagePacket
|
import ru.dbotthepony.kstarbound.network.packets.EntityMessagePacket
|
||||||
import ru.dbotthepony.kstarbound.network.packets.HitRequestPacket
|
import ru.dbotthepony.kstarbound.network.packets.HitRequestPacket
|
||||||
import ru.dbotthepony.kstarbound.network.packets.clientbound.SetPlayerStartPacket
|
import ru.dbotthepony.kstarbound.network.packets.clientbound.SetPlayerStartPacket
|
||||||
|
import ru.dbotthepony.kstarbound.util.ActionPacer
|
||||||
import ru.dbotthepony.kstarbound.util.BlockableEventLoop
|
import ru.dbotthepony.kstarbound.util.BlockableEventLoop
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
||||||
@ -587,11 +590,23 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun gravityAt(pos: IStruct2i): Vector2d {
|
fun gravityAt(pos: IStruct2i): Vector2d {
|
||||||
return template.worldParameters?.gravity ?: Vector2d.ZERO
|
val cell = getCell(pos)
|
||||||
|
return dungeonGravityInternal[cell.dungeonId] ?: template.worldParameters?.gravity ?: Vector2d.ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gravityAt(pos: IStruct2d): Vector2d {
|
fun gravityAt(pos: IStruct2d): Vector2d {
|
||||||
return template.worldParameters?.gravity ?: Vector2d.ZERO
|
val cell = getCell(pos.component1().toInt(), pos.component2().toInt())
|
||||||
|
return dungeonGravityInternal[cell.dungeonId] ?: template.worldParameters?.gravity ?: Vector2d.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isBreathable(pos: IStruct2i): Boolean {
|
||||||
|
val cell = getCell(pos)
|
||||||
|
return dungeonBreathableInternal.getOrDefault(cell.dungeonId, template.worldParameters?.airless != true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isBreathable(pos: IStruct2d): Boolean {
|
||||||
|
val cell = getCell(pos.component1().toInt(), pos.component2().toInt())
|
||||||
|
return dungeonBreathableInternal.getOrDefault(cell.dungeonId, template.worldParameters?.airless != true)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class LiquidLevel(val type: Registry.Entry<LiquidDefinition>, val average: Double)
|
data class LiquidLevel(val type: Registry.Entry<LiquidDefinition>, val average: Double)
|
||||||
@ -622,7 +637,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
/**
|
/**
|
||||||
* returns unapplied tile modifications
|
* returns unapplied tile modifications
|
||||||
*/
|
*/
|
||||||
abstract fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean = false): List<Pair<Vector2i, TileModification>>
|
abstract fun applyTileModifications(modifications: Collection<Pair<Vector2i, TileModification>>, allowEntityOverlap: Boolean, ignoreTileProtection: Boolean = false): CompletableFuture<List<Pair<Vector2i, TileModification>>>
|
||||||
|
|
||||||
fun addDamageNotification(notification: DamageNotificationPacket) {
|
fun addDamageNotification(notification: DamageNotificationPacket) {
|
||||||
pushRemoteDamageNotification(notification)
|
pushRemoteDamageNotification(notification)
|
||||||
@ -719,6 +734,15 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun environmentStatusEffects(pos: Vector2i): Set<String> {
|
||||||
|
// TODO: judging by original code, they wanted to allow definition
|
||||||
|
// of custom environmental effects per dungeon id
|
||||||
|
// But it never happened
|
||||||
|
return template.worldParameters?.environmentStatusEffects ?: setOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun damageTiles(positions: Collection<IStruct2i>, isBackground: Boolean, sourcePosition: Vector2d, damage: TileDamage, source: AbstractEntity? = null): CompletableFuture<TileDamageResult>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.api
|
package ru.dbotthepony.kstarbound.world.api
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
|
|
||||||
// uint8_t
|
// uint8_t
|
||||||
enum class TileColor {
|
enum class TileColor : IStringSerializable {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
RED,
|
RED,
|
||||||
BLUE,
|
BLUE,
|
||||||
@ -14,7 +15,7 @@ enum class TileColor {
|
|||||||
BLACK,
|
BLACK,
|
||||||
WHITE;
|
WHITE;
|
||||||
|
|
||||||
val lowercase = name.lowercase()
|
override val jsonName = name.lowercase()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val map: ImmutableMap<String, TileColor>
|
private val map: ImmutableMap<String, TileColor>
|
||||||
|
Loading…
Reference in New Issue
Block a user