explosion particles

This commit is contained in:
Bixilon 2021-05-26 22:55:01 +02:00
parent 53d7789e15
commit 483fdd80f2
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
22 changed files with 475 additions and 94 deletions

View File

@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.mappings.particle
import com.google.gson.JsonObject
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.data.mappings.registry.RegistryItem
import de.bixilon.minosoft.data.mappings.registry.ResourceLocationDeserializer
import de.bixilon.minosoft.data.mappings.versions.Registries
@ -22,12 +23,17 @@ import de.bixilon.minosoft.gui.rendering.textures.Texture
data class ParticleType(
override val resourceLocation: ResourceLocation,
val textures: List<ResourceLocation>,
val overrideLimiter: Boolean = false,
) : RegistryItem {
override fun toString(): String {
return resourceLocation.full
}
fun simple(): ParticleData {
return ParticleData(this)
}
companion object : ResourceLocationDeserializer<ParticleType> {
override fun deserialize(mappings: Registries?, resourceLocation: ResourceLocation, data: JsonObject): ParticleType {
val textures: MutableList<ResourceLocation> = mutableListOf()
@ -37,7 +43,7 @@ data class ParticleType(
textures += Texture.getResourceTextureIdentifier(textureResourceLocation.namespace, textureName = "particle/${textureResourceLocation.path}")
}
}
return ParticleType(resourceLocation, textures.toList())
return ParticleType(resourceLocation, textures.toList(), data["override_limiter"]?.asBoolean ?: false)
}
}
}

View File

@ -17,6 +17,7 @@ import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.ResourceLocationAble
import de.bixilon.minosoft.data.mappings.versions.Registries
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import de.bixilon.minosoft.util.collections.Clearable
@ -51,6 +52,11 @@ open class Registry<T : RegistryItem>(
return get(ResourceLocation.getPathResourceLocation(resourceLocation))
}
open operator fun get(resourceLocation: ResourceLocationAble): T? {
return get(resourceLocation.resourceLocation)
}
open operator fun get(id: Int): T {
return idValueMap[id] ?: parentRegistry?.get(id) ?: throw NullPointerException("Can not find item with id $id")
}

View File

@ -92,6 +92,14 @@ class RGBColor(val rgba: Int) : ChatCode {
return RGBColor(this)
}
fun Int.asGray(): RGBColor {
return RGBColor(this, this, this)
}
fun Float.asGray(): RGBColor {
return RGBColor(this, this, this)
}
fun mix(vararg colors: RGBColor): RGBColor {
var red = 0
var green = 0

View File

@ -78,4 +78,6 @@ object RenderConstants {
const val DOUBLE_PRESS_DELAY_BETWEEN_PRESSED = 500
const val MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE = 5.0f
const val MAXIMUM_PARTICLE_AMOUNT = 200000
}

View File

@ -0,0 +1,39 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle
import de.bixilon.minosoft.gui.rendering.particle.types.norender.ExplosionEmitterParticle
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.ExplosionParticle
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
import de.bixilon.minosoft.modding.event.events.ExplosionEvent
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
object DefaultParticleBehavior {
fun register(connection: PlayConnection, particleRenderer: ParticleRenderer) {
val explosionParticleType = connection.registries.particleTypeRegistry[ExplosionParticle]!!
val explosionEmitterParticleType = connection.registries.particleTypeRegistry[ExplosionEmitterParticle]!!
val invokers = listOf(
CallbackEventInvoker.of<ExplosionEvent> {
if (it.power >= 2.0f) {
particleRenderer.add(ExplosionEmitterParticle(connection, particleRenderer, it.position, explosionEmitterParticleType.simple()))
} else {
particleRenderer.add(ExplosionParticle(connection, particleRenderer, it.position, explosionParticleType.simple()))
}
}
)
connection.registerEvents(*invokers.toTypedArray())
}
}

View File

@ -14,9 +14,19 @@
package de.bixilon.minosoft.gui.rendering.particle
import de.bixilon.minosoft.data.mappings.DefaultFactory
import de.bixilon.minosoft.gui.rendering.particle.types.HappyVillagerParticle
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.ExplosionParticle
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.HappyVillagerParticle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
object DefaultParticleFactory : DefaultFactory<ParticleFactory<out Particle>>(
HappyVillagerParticle,
)
ExplosionParticle,
) {
fun build(particleType: ResourceLocation, particleRenderer: ParticleRenderer, connection: PlayConnection, position: Vec3, data: ParticleData = ParticleData(connection.registries.particleTypeRegistry[particleType]!!)) {
this[particleType]!!.build(connection, particleRenderer, position, data)
}
}

View File

@ -18,10 +18,9 @@ import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
import kotlin.random.Random
interface ParticleFactory<T : Particle> : CompanionResourceLocation {
fun build(connection: PlayConnection, position: Vec3, data: ParticleData, random: Random): T
fun build(connection: PlayConnection, particleRenderer: ParticleRenderer = connection.rendering!!.renderWindow[ParticleRenderer]!!, position: Vec3, data: ParticleData = ParticleData(connection.registries.particleTypeRegistry[RESOURCE_LOCATION]!!)): T
}

View File

@ -15,12 +15,13 @@ package de.bixilon.minosoft.gui.rendering.particle
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.RendererBuilder
import de.bixilon.minosoft.gui.rendering.modding.events.CameraMatrixChangeEvent
import de.bixilon.minosoft.gui.rendering.particle.types.ExplosionParticle
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.ExplosionParticle
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureArray
@ -61,14 +62,16 @@ class ParticleRenderer(
val random = Random.Default
val type = connection.registries.particleTypeRegistry[ExplosionParticle.RESOURCE_LOCATION]!!
for (i in 0 until 10000) {
val particle = ExplosionParticle(connection, Vec3(random.nextFloat(0.0f, 50.0f), random.nextFloat(6.0f, 50.0f), random.nextFloat(0.0f, 50.0f)), ParticleData(type), Random(random.nextLong()))
for (i in 0 until 20) {
val particle = ExplosionParticle(connection, this, Vec3(random.nextFloat(0.0f, 10.0f), random.nextFloat(6.0f, 10.0f), random.nextFloat(0.0f, 10.0f)), ParticleData(type))
// particle.grow(0.5f, 20000L)
// particle.velocity = Vec3(1f, 0.2f, 1f)
// particle.friction = Vec3(0.1f, 0.1f, 0.1f)
particle.relativeHover(-1.0f, 1.0f)
particles += particle
}
DefaultParticleBehavior.register(connection, this)
}
override fun postInit() {
@ -81,12 +84,16 @@ class ParticleRenderer(
renderWindow.textures.animator.use(particleShader)
}
var last = 0L
fun add(particle: Particle) {
check(particles.size < RenderConstants.MAXIMUM_PARTICLE_AMOUNT) { "Can not add particle: Limit reached (${particles.size} > ${RenderConstants.MAXIMUM_PARTICLE_AMOUNT}" }
particles += particle
}
override fun draw() {
particleShader.use()
val time = System.currentTimeMillis()
particleMesh.unload()
particleMesh = ParticleMesh()
@ -100,7 +107,6 @@ class ParticleRenderer(
}
particleMesh.load()
last = time
particleMesh.draw()
}

View File

@ -14,36 +14,40 @@
package de.bixilon.minosoft.gui.rendering.particle.types
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.particle.ParticleMesh
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.VecUtil.ONE
import de.bixilon.minosoft.gui.rendering.util.VecUtil.clear
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
import kotlin.math.abs
import kotlin.random.Random
abstract class Particle(protected val connection: PlayConnection, protected val position: Vec3, protected val data: ParticleData, protected val random: Random) {
protected var texture = connection.rendering!!.renderWindow.textures.allTextures[data.type.textures.first()]!!
protected var scale: Float = 0.1f
protected var color: RGBColor = ChatColors.WHITE
abstract class Particle(protected val connection: PlayConnection, protected val particleRenderer: ParticleRenderer, protected val position: Vec3, protected val data: ParticleData) {
protected val random = Random
private var lastTickTime = -1L
// growing
protected var nextScale: Float = scale
protected var scalePerMillisecond = -1.0f
// ageing
var dead = false
var age: Int = 0
protected set
var maxAge: Int = 10000 + random.nextInt(0, 10000)
var tickAge: Int
get() = age / ProtocolDefinition.TICK_TIME
set(value) {
age = value * ProtocolDefinition.TICK_TIME
}
var maxAge: Int = Integer.MAX_VALUE
var maxTickAge: Int
get() = maxAge / ProtocolDefinition.TICK_TIME
set(value) {
maxAge = value * ProtocolDefinition.TICK_TIME
}
// moving
var friction = Vec3.EMPTY
var velocity = Vec3.EMPTY
val friction = Vec3.EMPTY
val velocity = Vec3.EMPTY
// hover
protected var hovering = false
@ -51,36 +55,16 @@ abstract class Particle(protected val connection: PlayConnection, protected val
protected var hoverMaxY = 0.0f
fun grow(scale: Float, time: Long) {
nextScale = scale
scalePerMillisecond = (scale - this.scale) / time
}
protected var lastRealTickTime = 0L
private fun grow(deltaTime: Int) {
val deltaScale = nextScale - scale
if (abs(deltaScale) > GROW_LOWER_LIMIT) {
// we need to grow
val scaleAdd = scalePerMillisecond * deltaTime
// checke if the delta gets bigger (aka. we'd grew to much)
val nextScale = scale + scaleAdd
if (abs(this.nextScale - nextScale) > deltaScale) {
// abort scaling and avoid getting called another time
scale = nextScale
return
}
// we can grow
scale = nextScale
}
}
private fun move(deltaTime: Int) {
val perSecond = deltaTime / 1000.0f
position += velocity * perSecond
velocity = velocity * (Vec3.ONE - friction * perSecond)
velocity *= Vec3.ONE - friction * perSecond
if (velocity.length() < MINIMUM_VELOCITY) {
velocity = Vec3.EMPTY
velocity.clear()
}
}
@ -125,21 +109,8 @@ abstract class Particle(protected val connection: PlayConnection, protected val
}
}
private fun checkSpriteTexture() {
val totalTextures = data.type.textures.size
if (totalTextures <= 1) {
return
}
// calculate next texture
val nextTextureResourceLocation = data.type.textures[age / ((maxAge / totalTextures) + 1)]
if (texture.resourceLocation == nextTextureResourceLocation) {
return
}
texture = connection.rendering!!.renderWindow.textures.allTextures[nextTextureResourceLocation]!!
}
open fun tick() {
check(!dead) { "Cannot tick dead particle!" }
fun tick() {
val currentTime = System.currentTimeMillis()
if (lastTickTime == -1L) {
// never ticked before, skip
@ -147,8 +118,22 @@ abstract class Particle(protected val connection: PlayConnection, protected val
return
}
val deltaTime = (currentTime - lastTickTime).toInt()
check(deltaTime >= 0)
tick(deltaTime)
if (lastRealTickTime == -1L) {
lastRealTickTime = System.currentTimeMillis()
} else if (currentTime - lastRealTickTime >= ProtocolDefinition.TICK_TIME) {
realTick()
lastRealTickTime = currentTime
}
lastTickTime = currentTime
}
open fun tick(deltaTime: Int) {
check(!dead) { "Cannot tick dead particle!" }
check(deltaTime >= 0)
age += deltaTime
if (age >= maxAge) {
@ -156,22 +141,17 @@ abstract class Particle(protected val connection: PlayConnection, protected val
return
}
grow(deltaTime)
move(deltaTime)
hover(deltaTime)
checkSpriteTexture()
lastTickTime = currentTime
}
open fun addVertex(particleMesh: ParticleMesh) {
particleMesh.addVertex(position, scale, texture, color)
open fun realTick() {
}
abstract fun addVertex(particleMesh: ParticleMesh)
companion object {
const val GROW_LOWER_LIMIT = 0.001f
const val MINIMUM_VELOCITY = 0.01f
}
}

View File

@ -0,0 +1,52 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types.norender
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.ParticleFactory
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple.ExplosionParticle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import glm_.vec3.Vec3
class ExplosionEmitterParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : NoRenderParticle(connection, particleRenderer, position, data) {
val explosionParticleType = connection.registries.particleTypeRegistry[ExplosionParticle]!!
init {
maxTickAge = 8
}
override fun realTick() {
super.realTick()
for (i in 0 until 6) {
val position = Vec3(
x = position.x + (random.nextFloat() - random.nextFloat()) * 4.0f,
y = position.y + (random.nextFloat() - random.nextFloat()) * 4.0f,
z = position.z + (random.nextFloat() - random.nextFloat()) * 4.0f
)
particleRenderer.add(ExplosionParticle(connection, particleRenderer, position, explosionParticleType.simple(), (tickAge.toFloat() / maxTickAge)))
}
}
companion object : ParticleFactory<ExplosionEmitterParticle> {
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:explosion_emitter".asResourceLocation()
override fun build(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData): ExplosionEmitterParticle {
return ExplosionEmitterParticle(connection, particleRenderer, position, data)
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types.norender
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.ParticleMesh
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
abstract class NoRenderParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : Particle(connection, particleRenderer, position, data) {
override fun addVertex(particleMesh: ParticleMesh) {}
}

View File

@ -0,0 +1,74 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types.render
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
import kotlin.math.abs
abstract class RenderParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : Particle(connection, particleRenderer, position, data) {
protected var scale: Float = 0.1f
protected var color: RGBColor = ChatColors.WHITE
// growing
protected var nextScale: Float = scale
protected var scalePerMillisecond = Float.NEGATIVE_INFINITY
override fun tick(deltaTime: Int) {
super.tick(deltaTime)
if (dead) {
return
}
grow(deltaTime)
}
fun grow(scale: Float, time: Long) {
nextScale = scale
scalePerMillisecond = (scale - this.scale) / time
}
private fun grow(deltaTime: Int) {
if (scalePerMillisecond == Float.NEGATIVE_INFINITY) {
return
}
val deltaScale = nextScale - scale
if (abs(deltaScale) > GROW_LOWER_LIMIT) {
// we need to grow
val scaleAdd = scalePerMillisecond * deltaTime
// checke if the delta gets bigger (aka. we'd grew to much)
val nextScale = scale + scaleAdd
if (abs(this.nextScale - nextScale) > deltaScale) {
// abort scaling and avoid getting called another time
scale = nextScale
scalePerMillisecond = Float.NEGATIVE_INFINITY
return
}
// we can grow
scale = nextScale
}
}
companion object {
const val GROW_LOWER_LIMIT = 0.001f
}
}

View File

@ -0,0 +1,31 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types.render.texture
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.ParticleMesh
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.render.RenderParticle
import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
abstract class TextureParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : RenderParticle(connection, particleRenderer, position, data) {
abstract val texture: Texture
override fun addVertex(particleMesh: ParticleMesh) {
particleMesh.addVertex(position, scale, texture, color)
}
}

View File

@ -11,23 +11,31 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types
package de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.data.text.RGBColor.Companion.asGray
import de.bixilon.minosoft.gui.rendering.particle.ParticleFactory
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import glm_.vec3.Vec3
import kotlin.random.Random
class ExplosionParticle(connection: PlayConnection, position: Vec3, data: ParticleData, random: Random) : Particle(connection, position, data, random) {
class ExplosionParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData, val power: Float = 1.0f) : SimpleTextureParticle(connection, particleRenderer, position, data) {
init {
maxTickAge = 6 + random.nextInt(4)
val gray = random.nextFloat() * 0.6f + 0.4f
color = gray.asGray()
scale = 2.0f * (1.0f - gray * 0.5f)
}
companion object : ParticleFactory<ExplosionParticle> {
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:explosion".asResourceLocation()
override fun build(connection: PlayConnection, position: Vec3, data: ParticleData, random: Random): ExplosionParticle {
return ExplosionParticle(connection, position, data, random)
override fun build(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData): ExplosionParticle {
return ExplosionParticle(connection, particleRenderer, position, data)
}
}
}

View File

@ -11,23 +11,23 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types
package de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.ParticleFactory
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import glm_.vec3.Vec3
import kotlin.random.Random
class HappyVillagerParticle(connection: PlayConnection, position: Vec3, data: ParticleData, random: Random) : Particle(connection, position, data, random) {
class HappyVillagerParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : SimpleTextureParticle(connection, particleRenderer, position, data) {
companion object : ParticleFactory<HappyVillagerParticle> {
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:happy_villager".asResourceLocation()
override fun build(connection: PlayConnection, position: Vec3, data: ParticleData, random: Random): HappyVillagerParticle {
return HappyVillagerParticle(connection, position, data, random)
override fun build(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData): HappyVillagerParticle {
return HappyVillagerParticle(connection, particleRenderer, position, data)
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple
import de.bixilon.minosoft.data.mappings.particle.data.ParticleData
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
import de.bixilon.minosoft.gui.rendering.particle.types.render.texture.TextureParticle
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3
abstract class SimpleTextureParticle(connection: PlayConnection, particleRenderer: ParticleRenderer, position: Vec3, data: ParticleData) : TextureParticle(connection, particleRenderer, position, data) {
override var texture = particleRenderer.renderWindow.textures.allTextures[data.type.textures.first()]!!
private fun checkSpriteTexture() {
val totalTextures = data.type.textures.size
if (totalTextures <= 1) {
return
}
// calculate next texture
val nextTextureResourceLocation = data.type.textures[age / (maxAge / totalTextures + 1)]
if (texture.resourceLocation == nextTextureResourceLocation) {
return
}
texture = particleRenderer.renderWindow.textures.allTextures[nextTextureResourceLocation]!!
}
override fun tick(deltaTime: Int) {
super.tick(deltaTime)
if (dead) {
return
}
checkSpriteTexture()
}
}

View File

@ -26,9 +26,7 @@ import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.VecUtil.center
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -113,9 +111,7 @@ class AudioPlayer(
}
})
connection.registerEvent(CallbackEventInvoker.of<PlaySoundEvent> {
playSoundEvent(it.soundEvent, it.position.toVec3, it.volume, it.pitch)
})
DefaultAudioBehavior.register(connection, this)
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "OpenAL loaded!" }
@ -124,10 +120,19 @@ class AudioPlayer(
latch.dec()
}
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
connection.registries.soundEventRegistry[resourceLocation]?.let { playSoundEvent(it, position?.center, volume, pitch) }
}
fun playSoundEvent(resourceLocation: ResourceLocation, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
connection.registries.soundEventRegistry[resourceLocation]?.let { playSoundEvent(it, position, volume, pitch) }
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3i? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
playSoundEvent(soundEvent, position?.center, volume, pitch)
}
fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
if (!initialized) {
return

View File

@ -0,0 +1,35 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.sound
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
import de.bixilon.minosoft.modding.event.CallbackEventInvoker
import de.bixilon.minosoft.modding.event.events.ExplosionEvent
import de.bixilon.minosoft.modding.event.events.PlaySoundEvent
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import kotlin.random.Random
object DefaultAudioBehavior {
val ENTITY_GENERIC_EXPLODE = "minecraft:entity.generic.explode".asResourceLocation()
fun register(connection: PlayConnection, audioPlayer: AudioPlayer) {
val invokers = listOf(
CallbackEventInvoker.of<PlaySoundEvent> { audioPlayer.playSoundEvent(it.soundEvent, it.position.toVec3, it.volume, it.pitch) },
CallbackEventInvoker.of<ExplosionEvent> { audioPlayer.playSoundEvent(ENTITY_GENERIC_EXPLODE, it.position, 4.0f, (1.0f + (Random.nextFloat() - Random.nextFloat()) * 0.2f) * 0.7f) },
)
connection.registerEvents(*invokers.toTypedArray())
}
}

View File

@ -55,6 +55,18 @@ object VecUtil {
}
}
fun Vec3.clear() {
x = 0.0f
y = 0.0f
z = 0.0f
}
fun Vec3.assign(vec3: Vec3) {
x = vec3.x
y = vec3.y
z = vec3.z
}
fun getRotatedValues(x: Float, y: Float, sin: Float, cos: Float, rescale: Boolean): Vec2 {
val result = Vec2(x * cos - y * sin, x * sin + y * cos)
if (rescale) {
@ -294,7 +306,7 @@ object VecUtil {
}
operator fun Vec3i.get(axis: Axes): Int {
return when(axis) {
return when (axis) {
Axes.X -> this.x
Axes.Y -> this.y
Axes.Z -> this.z

View File

@ -0,0 +1,29 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.modding.event.events
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.play.ExplosionS2CP
import glm_.vec3.Vec3
class ExplosionEvent(
connection: PlayConnection,
val position: Vec3,
val power: Float,
val explodedBlocks: List<Vec3>,
val velocity: Vec3,
) : PlayConnectionEvent(connection) {
constructor(connection: PlayConnection, packet: ExplosionS2CP) : this(connection, packet.position, packet.power, packet.explodedBlocks, packet.velocity)
}

View File

@ -86,6 +86,10 @@ abstract class Connection {
eventListeners.add(method)
}
open fun registerEvents(vararg method: EventInvoker) {
eventListeners.addAll(method)
}
val isDisconnected: Boolean
get() = connectionState == ConnectionStates.DISCONNECTING || connectionState == ConnectionStates.DISCONNECTED || connectionState == ConnectionStates.FAILED || connectionState == ConnectionStates.FAILED_NO_RETRY

View File

@ -12,6 +12,7 @@
*/
package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.modding.event.events.ExplosionEvent
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
@ -23,15 +24,15 @@ import glm_.vec3.Vec3i
class ExplosionS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val position = buffer.readFloatPosition()
val radius = buffer.readFloat()
val explodedBlocks: Array<Vec3> = buffer.readArray(buffer.readInt()) { Vec3(buffer.readByte(), buffer.readByte(), buffer.readByte()) }
val power = buffer.readFloat()
val explodedBlocks: List<Vec3> = buffer.readArray(buffer.readInt()) { Vec3(buffer.readByte(), buffer.readByte(), buffer.readByte()) }.toList()
val velocity = buffer.readFloatPosition()
override fun check(connection: PlayConnection) {
require(radius <= 100.0f) {
require(power <= 100.0f) {
// maybe somebody tries to make bullshit?
// Sorry, Maximilian Rosenmüller
"Explosion to big $radius > 100.0F"
"Explosion to big $power > 100.0F"
}
}
@ -40,10 +41,12 @@ class ExplosionS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val blockPosition = Vec3i(position + record)
connection.world.setBlockState(blockPosition, null)
}
connection.player.entity.velocity = velocity
connection.player.entity.velocity = connection.player.entity.velocity + velocity
connection.fireEvent(ExplosionEvent(connection, this))
}
override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Explosion (position=$position, radius=$radius, explodedBlocks=$explodedBlocks, velocity=$velocity)" }
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Explosion (position=$position, radius=$power, explodedBlocks=$explodedBlocks, velocity=$velocity)" }
}
}