Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Particles): Beautiful damage particles #5703

Open
wants to merge 10 commits into
base: nextgen
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ object ModuleManager : EventListener, Iterable<ClientModule> by modules {
ModuleCameraClip,
ModuleClickGui,
ModuleDamageParticles,
ModuleParticles,
ModuleESP,
ModuleFreeCam,
ModuleFreeLook,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package net.ccbluex.liquidbounce.features.module.modules.render

import com.mojang.blaze3d.systems.RenderSystem
import net.ccbluex.liquidbounce.config.types.NamedChoice
import net.ccbluex.liquidbounce.event.events.AttackEntityEvent
import net.ccbluex.liquidbounce.event.events.WorldRenderEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.ClientModule
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleParticles.color
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleParticles.mc
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleParticles.particleSize
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleParticles.rotate
import net.ccbluex.liquidbounce.features.module.modules.render.ModuleParticles.speed
import net.ccbluex.liquidbounce.render.WorldRenderEnvironment
import net.ccbluex.liquidbounce.render.drawCustomMesh
import net.ccbluex.liquidbounce.render.engine.Color4b
import net.ccbluex.liquidbounce.render.renderEnvironmentForWorld
import net.ccbluex.liquidbounce.utils.aiming.utils.canSeePointFrom
import net.ccbluex.liquidbounce.utils.client.Chronometer
import net.ccbluex.liquidbounce.utils.client.registerAsDynamicImageFromClientResources
import net.ccbluex.liquidbounce.utils.client.world
import net.ccbluex.liquidbounce.utils.combat.shouldBeShown
import net.ccbluex.liquidbounce.utils.kotlin.random
import net.ccbluex.liquidbounce.utils.kotlin.randomDouble
import net.ccbluex.liquidbounce.utils.math.interpolate
import net.ccbluex.liquidbounce.utils.math.times
import net.ccbluex.liquidbounce.utils.math.toBlockPos
import net.minecraft.client.gl.ShaderProgramKeys
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.util.Identifier
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.Vec3d
import org.joml.Quaternionf
import kotlin.math.max

/**
* Particles - красивые частички в мире.
*
* @author sqlerrorthing
*/
@Suppress("MagicNumber")
object ModuleParticles : ClientModule("Particles", category = Category.RENDER) {
val particleSize by float("Size", 1f, 0.5f..2f)
val speed by float("Speed", 1f, 0.5f..2f)
private val count by intRange("Count", 2..10, 2..30, "particles")
val rotate by boolean("RandomParticleRotation", true)
val color by color("Color", Color4b.RED)
private val image by enumChoice("Particle", ParticleImage.STAR)

private val particles = mutableListOf<Particle>()
private val chronometer = Chronometer()

@Suppress("unused")
private val attackEvent = handler<AttackEntityEvent> { event ->
if (!event.entity.shouldBeShown()
|| !chronometer.hasElapsed(230)
|| event.isCancelled
) {
return@handler
}

chronometer.reset()

val center = with (event.entity) {
boundingBox.center
}

repeat(count.random()) { _ ->
particles.add(Particle(center))
}
}

@Suppress("unused", "MagicNumber")
private val displayHandler = handler<WorldRenderEvent> { event ->
renderEnvironmentForWorld(event.matrixStack) {
RenderSystem.depthMask(true)
RenderSystem.disableCull()
mc.gameRenderer.lightmapTextureManager.disable()
RenderSystem.defaultBlendFunc()

RenderSystem.setShaderTexture(0, image.texture)

particles.removeIf { particle ->
val flag = particle.alpha <= 0 || player.pos.distanceTo(particle.pos) > 30
if (!flag) {
particle.update(event.partialTicks.toDouble())

mc.cameraEntity?.let { camera ->
if (canSeePointFrom(camera.eyePos, particle.pos)) {
matrixStack.push()
render(particle, event.partialTicks)
matrixStack.pop()
}
}
}

return@removeIf flag
}

RenderSystem.depthMask(true)
RenderSystem.enableCull()
RenderSystem.defaultBlendFunc()
mc.gameRenderer.lightmapTextureManager.enable()
}
}
}

@Suppress("UNUSED")
private enum class ParticleImage(
override val choiceName: String,
val texture: Identifier
) : NamedChoice {
/**
* Original: IDK (first: https://github.com/CCBlueX/LiquidBounce/pull/4976)
*/
ORBIZ("Orbiz", "particles/glow.png".registerAsDynamicImageFromClientResources()),

/**
* Original: https://www.svgrepo.com/svg/528677/stars-minimalistic
* Modified: @sqlerrorthing
*/
STAR("Star", "particles/star.png".registerAsDynamicImageFromClientResources()),

/**
* Original: https://www.svgrepo.com/svg/487288/dollar?edit=true
* Modified: @sqlerrorthing
*/
DOLLAR("Dollar", "particles/dollar.png".registerAsDynamicImageFromClientResources())
}

@Suppress("MagicNumber", "LongParameterList")
class Particle private constructor(
var pos: Vec3d,
var prevPos: Vec3d,
var velocity: Vec3d,
var collisionTime: Long = -1,
var alpha: Float = 1.0f, /* 0 <= alpha <= 1 */
val spawnTime: Long = System.currentTimeMillis(),
val rotation: Float
) {
constructor(pos: Vec3d) : this(
pos = pos,
prevPos = pos,
velocity = Vec3d(
(-0.01..0.01).randomDouble(),
(0.01..0.02).randomDouble(),
(-0.01..0.01).randomDouble()
),
rotation = (0f..360f).random().toFloat()
)
}

@Suppress("MagicNumber", "NOTHING_TO_INLINE", "UnusedParameter")
inline fun Particle.update(delta: Double) {
val particleSpeed = speed.toDouble()
prevPos = pos

if (collisionTime != -1L) {
val timeSinceCollision = System.currentTimeMillis() - collisionTime
alpha = max(0f, 1f - (timeSinceCollision / 3000f))
}

velocity = velocity.add(0.0, -0.0001, 0.0)
val nextPos = pos.add((velocity * delta).multiply(particleSpeed, 1.0, particleSpeed))

if (!nextPos.isBlockAir) {
if (collisionTime == -1L) {
collisionTime = System.currentTimeMillis()
}

val dx = velocity.x * delta * particleSpeed
val dy = velocity.y * delta
val dz = velocity.z * delta * particleSpeed

if (!Vec3d(pos.x + dx, pos.y, pos.z).isBlockAir) {
velocity = Vec3d(0.0, velocity.y, velocity.z)
}

if (!Vec3d(pos.x, pos.y + dy, pos.z).isBlockAir) {
velocity = Vec3d(velocity.x, -velocity.y * 0.5, velocity.z)
}

if (!Vec3d(pos.x, pos.y, pos.z + dz).isBlockAir) {
velocity = Vec3d(velocity.x, velocity.y, 0.0)
}

pos = pos.add((velocity * delta).multiply(particleSpeed, 1.0, particleSpeed))
} else {
pos = nextPos
}
}

@Suppress("MagicNumber", "NOTHING_TO_INLINE", "UnusedParameter")
inline fun WorldRenderEnvironment.render(particle: Particle, partialTicks: Float) {
with(mc.gameRenderer.camera.pos) {
matrixStack.translate(-this.x, -this.y, -this.z)
}

with(particle.pos.interpolate(particle.prevPos, partialTicks.toDouble())) {
matrixStack.translate(x, y, z)
}

val size = particleSize * 0.25f * (1 - (System.currentTimeMillis() - particle.spawnTime) / 12000f)
val rotation = if (rotate) {
(particle.rotation + 90f) % 360f
} else {
90f
}

with (matrixStack) {
translate(-size / 2.0, -size / 2.0, 0.0)
multiply(mc.gameRenderer.camera.rotation)
scale(-1.0f, 1.0f, -1.0f)
multiply(Quaternionf().fromAxisAngleDeg(0.0f, 0.0f, 1.0f, rotation))
translate(size / 2.0, size / 2.0, 0.0)
}

val renderColor = color.alpha(MathHelper.clamp((particle.alpha * color.a.toFloat()).toInt(), 0, color.a))

drawCustomMesh(
VertexFormat.DrawMode.QUADS,
VertexFormats.POSITION_TEXTURE_COLOR,
ShaderProgramKeys.POSITION_TEX_COLOR
) { matrix ->
vertex(matrix, 0.0f, -size, 0.0f)
.texture(0.0f, 0.0f)
.color(renderColor.toARGB())

vertex(matrix, -size, -size, 0.0f)
.texture(0.0f, 1.0f)
.color(renderColor.toARGB())

vertex(matrix, -size, 0.0f, 0.0f)
.texture(1.0f, 1.0f)
.color(renderColor.toARGB())

vertex(matrix, 0.0f, 0.0f, 0.0f)
.texture(1.0f, 0.0f)
.color(renderColor.toARGB())
}
}

inline val Vec3d.isBlockAir: Boolean get() =
world.getBlockState(this.toBlockPos()).isAir
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@file:Suppress("NOTHING_TO_INLINE")
package net.ccbluex.liquidbounce.utils.client

import net.ccbluex.liquidbounce.LiquidBounce
import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.util.Identifier
import java.io.InputStream

/**
* @param path prefix /resources/liquidbounce/$path
*/
inline fun Identifier.registerDynamicImageFromResources(path: String) {
with(LiquidBounce.javaClass.getResourceAsStream("/resources/liquidbounce/$path")!!) {
[email protected](this)
}
}

@Suppress("MagicNumber")
inline fun String.registerAsDynamicImageFromClientResources(): Identifier =
Identifier.of("liquidbounce", "dynamic-texture-" + System.currentTimeMillis().toString(36)).apply {
registerDynamicImageFromResources(this@registerAsDynamicImageFromClientResources)
}

inline fun Identifier.registerDynamicImage(image: InputStream) {
this.registerDynamicImage(NativeImage.read(image))
}

inline fun Identifier.registerDynamicImage(image: NativeImage) {
mc.textureManager.registerTexture(this, NativeImageBackedTexture(image))
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ inline operator fun Vec3d.times(scalar: Double): Vec3d {
return this.multiply(scalar)
}

inline fun Vec3d.interpolate(start: Vec3d, multiple: Double) =
Vec3d(
this.x.interpolate(start.x, multiple),
this.y.interpolate(start.y, multiple),
this.z.interpolate(start.z, multiple),
)

inline fun Double.interpolate(old: Double, scale: Double) = old + (this - old) * scale

inline fun Vec3d.copy(x: Double = this.x, y: Double = this.y, z: Double = this.z) = Vec3d(x, y, z)

inline operator fun Vec3d.component1(): Double = this.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,26 @@ package net.ccbluex.liquidbounce.utils.render

import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem
import net.ccbluex.liquidbounce.LiquidBounce
import net.ccbluex.liquidbounce.config.types.Choice
import net.ccbluex.liquidbounce.config.types.ChoiceConfigurable
import net.ccbluex.liquidbounce.config.types.ToggleableConfigurable
import net.ccbluex.liquidbounce.features.module.ClientModule
import net.ccbluex.liquidbounce.render.*
import net.ccbluex.liquidbounce.render.engine.Color4b
import net.ccbluex.liquidbounce.render.engine.Vec3
import net.ccbluex.liquidbounce.utils.client.registerAsDynamicImageFromClientResources
import net.ccbluex.liquidbounce.utils.entity.box
import net.ccbluex.liquidbounce.utils.entity.interpolateCurrentPosition
import net.ccbluex.liquidbounce.utils.entity.lastRenderPos
import net.ccbluex.liquidbounce.utils.math.interpolate
import net.ccbluex.liquidbounce.utils.math.plus
import net.ccbluex.liquidbounce.utils.render.WorldToScreen.calculateScreenPos
import net.minecraft.client.gl.ShaderProgramKeys
import net.minecraft.client.render.VertexFormat
import net.minecraft.client.render.VertexFormats
import net.minecraft.client.texture.NativeImage
import net.minecraft.client.texture.NativeImageBackedTexture
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.RotationAxis
Expand Down Expand Up @@ -83,15 +81,7 @@ class WorldTargetRenderer(module: ClientModule) : TargetRenderer<WorldRenderEnvi

inner class Ghost : WorldTargetRenderAppearance("Ghost") {

private val glow by lazy {
Identifier.of("liquidbounce", "glow").also { identifier ->
val texture = with(LiquidBounce.javaClass.getResourceAsStream("/resources/liquidbounce/glow.png")) {
NativeImageBackedTexture(NativeImage.read(this))
}

mc.textureManager.registerTexture(identifier, texture)
}
}
private val glow = "particles/glow.png".registerAsDynamicImageFromClientResources()

private var lastTime = System.currentTimeMillis()

Expand Down Expand Up @@ -216,15 +206,6 @@ class WorldTargetRenderer(module: ClientModule) : TargetRenderer<WorldRenderEnvi
}
}
}

private fun Vec3d.interpolate(start: Vec3d, multiple: Double) =
Vec3d(
this.x.interpolate(start.x, multiple),
this.y.interpolate(start.y, multiple),
this.z.interpolate(start.z, multiple),
)

private fun Double.interpolate(old: Double, scale: Double) = old + (this - old) * scale
}

inner class Legacy : WorldTargetRenderAppearance("Legacy") {
Expand Down
Binary file removed src/main/resources/resources/liquidbounce/glow.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading