Now root mechanics is properly handled by damageTiles

This commit is contained in:
DBotThePony 2024-04-22 17:51:21 +07:00
parent b50f356f7e
commit d9de575de6
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 70 additions and 32 deletions

View File

@ -3,7 +3,8 @@ package ru.dbotthepony.kstarbound.server.world
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import it.unimi.dsi.fastutil.ints.IntArraySet import it.unimi.dsi.fastutil.ints.IntArraySet
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.ObjectArraySet import it.unimi.dsi.fastutil.objects.ObjectArraySet
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asCompletableFuture
@ -52,7 +53,6 @@ import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.RejectedExecutionException import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.function.Supplier import java.util.function.Supplier
import java.util.stream.Collectors
class ServerWorld private constructor( class ServerWorld private constructor(
val server: StarboundServer, val server: StarboundServer,
@ -169,7 +169,9 @@ class ServerWorld private constructor(
if (damage.amount <= 0.0) if (damage.amount <= 0.0)
return TileDamageResult.NONE return TileDamageResult.NONE
val actualPositions = ObjectArraySet<Pair<Vector2i, ServerChunk?>>() data class TileToDamage(val position: Vector2i, val chunk: ServerChunk?, val roots: MutableList<TileEntity> = ArrayList(0))
val actualPositions = ObjectArraySet<TileToDamage>()
for (pos in positions) { for (pos in positions) {
val wrapped = geometry.wrap(pos) val wrapped = geometry.wrap(pos)
@ -179,58 +181,92 @@ class ServerWorld private constructor(
val cell = chunk.getCell(wrapped - chunk.pos.tile) val cell = chunk.getCell(wrapped - chunk.pos.tile)
if (cell.rootSource != null) { if (cell.rootSource != null) {
actualPositions.add(geometry.wrap(cell.rootSource!!) to chunk) actualPositions.add(TileToDamage(wrapped, chunk, entityIndex.tileEntitiesAt(geometry.wrap(cell.rootSource!!))))
} else { } else {
actualPositions.add(wrapped to chunk) actualPositions.add(TileToDamage(wrapped, chunk))
} }
} else { } else {
actualPositions.add(wrapped to chunk) actualPositions.add(TileToDamage(wrapped, chunk))
} }
} }
var topMost = TileDamageResult.NONE var topMost = TileDamageResult.NONE
val damagedEntities = ObjectArraySet<TileEntity>() val toDamageEntities = Object2ObjectArrayMap<TileEntity, ObjectArraySet<Vector2i>>()
val entityDamageResults = HashMap<Vector2i, TileDamageResult>()
for ((pos, chunk) in actualPositions) { for ((pos, chunk, roots) in actualPositions) {
var damage = damage for (root in roots) {
var tileEntityResult = TileDamageResult.NONE toDamageEntities.computeIfAbsent(root, Object2ObjectFunction { ObjectArraySet() }).add(pos)
val getCell = chunk?.getCell(pos - chunk.pos.tile)
if (getCell != null) {
if (getCell.dungeonId in protectedDungeonIDs) {
damage = damage.copy(type = TileDamageType.PROTECTED)
}
} }
if (!isBackground) { if (!isBackground) {
for (entity in entitiesAtTile(pos)) { for (entity in entitiesAtTile(pos)) {
if (!damagedEntities.add(entity)) continue toDamageEntities.computeIfAbsent(entity, Object2ObjectFunction { ObjectArraySet() }).add(pos)
}
}
}
val occupySpaces = entity.occupySpaces.stream() val killedEntities = ObjectArraySet<TileEntity>()
.filter { p -> actualPositions.any { it.first == p } }
.toList()
pacer?.consume(10) for ((entity, damagePositions) in toDamageEntities.entries) {
val broken = entity.damage(occupySpaces, sourcePosition, damage) var actualDamage = damage
if (source != null && broken) { for (pos in damagePositions) {
source.receiveMessage("tileEntityBroken", jsonArrayOf(pos, entity.type.jsonName, (entity as? WorldObject)?.config?.key)) val cell = getCell(pos)
}
if (damage.type == TileDamageType.PROTECTED) if (cell.dungeonId in protectedDungeonIDs) {
tileEntityResult = TileDamageResult.PROTECTED actualDamage = actualDamage.copy(type = TileDamageType.PROTECTED)
else entityDamageResults[pos] = TileDamageResult.PROTECTED
tileEntityResult = TileDamageResult.NORMAL } else {
entityDamageResults[pos] = TileDamageResult.NORMAL
}
}
val occupySpaces = entity.occupySpaces.stream()
.filter { p -> p in damagePositions }
.toList()
pacer?.consume(10)
val broken = entity.damage(occupySpaces, sourcePosition, actualDamage)
if (source != null && broken) {
source.receiveMessage("tileEntityBroken", jsonArrayOf(
damagePositions.firstOrNull { p -> actualPositions.any { it.position == p } } ?: entity.tilePosition,
entity.type.jsonName,
(entity as? WorldObject)?.config?.key))
}
if (broken) {
killedEntities.add(entity)
for (pos in damagePositions) {
entityDamageResults.remove(pos)
}
}
}
actualPositions.removeIf {
it.roots.removeIf { it in killedEntities }
it.roots.isNotEmpty()
}
for ((pos, chunk) in actualPositions) {
var actualDamage = damage
val tileEntityResult = entityDamageResults[pos] ?: TileDamageResult.NONE
val getCell = chunk?.getCell(pos - chunk.pos.tile)
if (getCell != null) {
if (getCell.dungeonId in protectedDungeonIDs) {
actualDamage = actualDamage.copy(type = TileDamageType.PROTECTED)
} }
} }
// Penetrating damage should carry through to the blocks behind this // Penetrating damage should carry through to the blocks behind this
// entity. // entity.
if (tileEntityResult == TileDamageResult.NONE || damage.type.isPenetrating) { if (tileEntityResult == TileDamageResult.NONE || actualDamage.type.isPenetrating) {
chunk ?: continue chunk ?: continue
pacer?.consume() pacer?.consume()
val (result, health, tile) = chunk.damageTile(pos - chunk.pos.tile, isBackground, sourcePosition, damage, source) val (result, health, tile) = chunk.damageTile(pos - chunk.pos.tile, isBackground, sourcePosition, actualDamage, source)
topMost = topMost.coerceAtLeast(result) topMost = topMost.coerceAtLeast(result)
if (source != null && health?.isDead == true) { if (source != null && health?.isDead == true) {

View File

@ -717,6 +717,8 @@ class PlantEntity() : TileEntity() {
} else { } else {
remove(RemovalReason.DYING) remove(RemovalReason.DYING)
} }
return true
} }
return false return false