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.JsonObject
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 kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
@ -52,7 +53,6 @@ import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
import java.util.stream.Collectors
class ServerWorld private constructor(
val server: StarboundServer,
@ -169,7 +169,9 @@ class ServerWorld private constructor(
if (damage.amount <= 0.0)
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) {
val wrapped = geometry.wrap(pos)
@ -179,58 +181,92 @@ class ServerWorld private constructor(
val cell = chunk.getCell(wrapped - chunk.pos.tile)
if (cell.rootSource != null) {
actualPositions.add(geometry.wrap(cell.rootSource!!) to chunk)
actualPositions.add(TileToDamage(wrapped, chunk, entityIndex.tileEntitiesAt(geometry.wrap(cell.rootSource!!))))
} else {
actualPositions.add(wrapped to chunk)
actualPositions.add(TileToDamage(wrapped, chunk))
}
} else {
actualPositions.add(wrapped to chunk)
actualPositions.add(TileToDamage(wrapped, chunk))
}
}
var topMost = TileDamageResult.NONE
val damagedEntities = ObjectArraySet<TileEntity>()
val toDamageEntities = Object2ObjectArrayMap<TileEntity, ObjectArraySet<Vector2i>>()
val entityDamageResults = HashMap<Vector2i, TileDamageResult>()
for ((pos, chunk) in actualPositions) {
var damage = damage
var tileEntityResult = TileDamageResult.NONE
val getCell = chunk?.getCell(pos - chunk.pos.tile)
if (getCell != null) {
if (getCell.dungeonId in protectedDungeonIDs) {
damage = damage.copy(type = TileDamageType.PROTECTED)
}
for ((pos, chunk, roots) in actualPositions) {
for (root in roots) {
toDamageEntities.computeIfAbsent(root, Object2ObjectFunction { ObjectArraySet() }).add(pos)
}
if (!isBackground) {
for (entity in entitiesAtTile(pos)) {
if (!damagedEntities.add(entity)) continue
toDamageEntities.computeIfAbsent(entity, Object2ObjectFunction { ObjectArraySet() }).add(pos)
}
}
}
val occupySpaces = entity.occupySpaces.stream()
.filter { p -> actualPositions.any { it.first == p } }
.toList()
val killedEntities = ObjectArraySet<TileEntity>()
pacer?.consume(10)
val broken = entity.damage(occupySpaces, sourcePosition, damage)
for ((entity, damagePositions) in toDamageEntities.entries) {
var actualDamage = damage
if (source != null && broken) {
source.receiveMessage("tileEntityBroken", jsonArrayOf(pos, entity.type.jsonName, (entity as? WorldObject)?.config?.key))
}
for (pos in damagePositions) {
val cell = getCell(pos)
if (damage.type == TileDamageType.PROTECTED)
tileEntityResult = TileDamageResult.PROTECTED
else
tileEntityResult = TileDamageResult.NORMAL
if (cell.dungeonId in protectedDungeonIDs) {
actualDamage = actualDamage.copy(type = TileDamageType.PROTECTED)
entityDamageResults[pos] = TileDamageResult.PROTECTED
} 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
// entity.
if (tileEntityResult == TileDamageResult.NONE || damage.type.isPenetrating) {
if (tileEntityResult == TileDamageResult.NONE || actualDamage.type.isPenetrating) {
chunk ?: continue
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)
if (source != null && health?.isDead == true) {

View File

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