From 4ecda8b2eb0f5c7eeae429c7a4e0807b795ac2eb Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Tue, 19 Mar 2024 14:38:58 +0300 Subject: [PATCH 01/46] Rendering Base --- .../mixin/render/GlStateManagerMixin.java | 49 +++++ .../lambda/mixin/render/InGameHudMixin.java | 17 ++ .../mixin/render/VertexBufferMixin.java | 25 +++ .../com/lambda/command/CommandManager.kt | 3 +- .../lambda/command/commands/ModuleCommand.kt | 9 +- .../com/lambda/event/events/RenderEvent.kt | 12 ++ .../kotlin/com/lambda/graphics/RenderMain.kt | 39 ++++ .../graphics/buffer/vao/IRenderContext.kt | 24 +++ .../com/lambda/graphics/buffer/vao/VAO.kt | 202 ++++++++++++++++++ .../buffer/vao/vertex/VertexAttrib.kt | 18 ++ .../graphics/buffer/vao/vertex/VertexMode.kt | 9 + .../kotlin/com/lambda/graphics/gl/GLObject.kt | 5 + .../com/lambda/graphics/gl/GlStateUtils.kt | 69 ++++++ .../kotlin/com/lambda/graphics/gl/Matrices.kt | 29 +++ .../com/lambda/graphics/gl/MemoryUtils.kt | 61 ++++++ .../kotlin/com/lambda/graphics/gl/VaoUtils.kt | 44 ++++ .../com/lambda/graphics/shader/Shader.kt | 62 ++++++ .../com/lambda/graphics/shader/ShaderType.kt | 10 + .../com/lambda/graphics/shader/ShaderUtils.kt | 82 +++++++ .../com/lambda/module/modules/client/HUD.kt | 13 ++ .../module/modules/movement/RocketExtend.kt | 2 +- .../kotlin/com/lambda/util/Communication.kt | 3 +- .../kotlin/com/lambda/util/LambdaResource.kt | 6 + .../main/kotlin/com/lambda/util/math/Color.kt | 19 ++ .../main/kotlin/com/lambda/util/math/Vec2d.kt | 32 +++ .../main/kotlin/com/lambda/util/text/Color.kt | 184 ---------------- .../kotlin/com/lambda/util/text/StyleDsl.kt | 12 +- .../kotlin/com/lambda/util/text/TextDsl.kt | 12 +- .../lambda/shaders/fragment/pos_color.frag | 8 + .../lambda/shaders/vertex/pos_color.vert | 14 ++ .../src/main/resources/lambda.accesswidener | 4 + .../main/resources/lambda.mixins.common.json | 5 +- 32 files changed, 882 insertions(+), 201 deletions(-) create mode 100644 common/src/main/java/com/lambda/mixin/render/GlStateManagerMixin.java create mode 100644 common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java create mode 100644 common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java create mode 100644 common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/RenderMain.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexMode.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/GLObject.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/shader/ShaderType.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt create mode 100644 common/src/main/kotlin/com/lambda/util/LambdaResource.kt create mode 100644 common/src/main/kotlin/com/lambda/util/math/Color.kt create mode 100644 common/src/main/kotlin/com/lambda/util/math/Vec2d.kt delete mode 100644 common/src/main/kotlin/com/lambda/util/text/Color.kt create mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag create mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert diff --git a/common/src/main/java/com/lambda/mixin/render/GlStateManagerMixin.java b/common/src/main/java/com/lambda/mixin/render/GlStateManagerMixin.java new file mode 100644 index 000000000..82e2b29ad --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/render/GlStateManagerMixin.java @@ -0,0 +1,49 @@ +package com.lambda.mixin.render; + +import com.lambda.graphics.gl.GlStateUtils; +import com.mojang.blaze3d.platform.GlStateManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL11.GL_CULL_FACE; + +@Mixin(GlStateManager.class) +public class GlStateManagerMixin { + @Inject(method = "_enableDepthTest", at = @At("TAIL"), remap = false) + private static void depthTestEnable(CallbackInfo ci) { + GlStateUtils.capSet(GL_DEPTH_TEST, true); + } + + @Inject(method = "_disableDepthTest", at = @At("TAIL"), remap = false) + private static void depthTestDisable(CallbackInfo ci) { + GlStateUtils.capSet(GL_DEPTH_TEST, false); + } + + @Inject(method = "_depthMask", at = @At("TAIL"), remap = false) + private static void depthMask(boolean mask, CallbackInfo ci) { + GlStateUtils.capSet(GL_DEPTH, mask); + } + + @Inject(method = "_enableBlend", at = @At("TAIL"), remap = false) + private static void blendEnable(CallbackInfo ci) { + GlStateUtils.capSet(GL_BLEND, true); + } + + @Inject(method = "_disableBlend", at = @At("TAIL"), remap = false) + private static void blendDisable(CallbackInfo ci) { + GlStateUtils.capSet(GL_BLEND, false); + } + + @Inject(method = "_enableCull", at = @At("TAIL"), remap = false) + private static void cullEnable(CallbackInfo ci) { + GlStateUtils.capSet(GL_CULL_FACE, true); + } + + @Inject(method = "_disableCull", at = @At("TAIL"), remap = false) + private static void cullDisable(CallbackInfo ci) { + GlStateUtils.capSet(GL_CULL_FACE, false); + } +} diff --git a/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java new file mode 100644 index 000000000..f98c97947 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/render/InGameHudMixin.java @@ -0,0 +1,17 @@ +package com.lambda.mixin.render; + +import com.lambda.graphics.RenderMain; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.InGameHud; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(InGameHud.class) +public class InGameHudMixin { + @Inject(method = "render", at = @At("TAIL")) + private void onRender(DrawContext context, float tickDelta, CallbackInfo ci) { + RenderMain.render2D(); + } +} diff --git a/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java b/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java new file mode 100644 index 000000000..6c3584660 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java @@ -0,0 +1,25 @@ +package com.lambda.mixin.render; + +import com.lambda.graphics.gl.VaoUtils; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gl.VertexBuffer; +import net.minecraft.client.render.BufferBuilder; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.nio.ByteBuffer; + +@Mixin(VertexBuffer.class) +public class VertexBufferMixin { + @Shadow + private int indexBufferId; + + @Inject(method = "uploadIndexBuffer", at = @At("RETURN")) + private void onConfigureIndexBuffer(BufferBuilder.DrawParameters parameters, ByteBuffer vertexBuffer, CallbackInfoReturnable cir) { + RenderSystem.ShapeIndexBuffer value = cir.getReturnValue(); + VaoUtils.lastIbo = value == null ? this.indexBufferId : value.id; + } +} diff --git a/common/src/main/kotlin/com/lambda/command/CommandManager.kt b/common/src/main/kotlin/com/lambda/command/CommandManager.kt index 6c8fd8233..e2c9c8f09 100644 --- a/common/src/main/kotlin/com/lambda/command/CommandManager.kt +++ b/common/src/main/kotlin/com/lambda/command/CommandManager.kt @@ -20,6 +20,7 @@ import org.reflections.Reflections import org.reflections.scanners.Scanners import org.reflections.util.ClasspathHelper import org.reflections.util.ConfigurationBuilder +import java.awt.Color import kotlin.math.max import kotlin.math.min @@ -95,7 +96,7 @@ object CommandManager : Configurable(LambdaConfig), Loadable { val position = min(syntax.input.length, syntax.cursor) player.sendMessage(buildText { clickEvent(suggestCommand("$prefix${reader.string}")) { - color(Color.GREY) { + color(Color.GRAY) { if (position > ERROR_PADDING) { literal("...") } diff --git a/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt index 04c40c919..b151c9b61 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt @@ -19,6 +19,7 @@ import com.lambda.util.Communication.warn import com.lambda.util.StringUtils import com.lambda.util.text.* import com.lambda.util.text.ClickEvents.suggestCommand +import java.awt.Color object ModuleCommand : LambdaCommand() { override val name = "module" @@ -42,7 +43,7 @@ object ModuleCommand : LambdaCommand() { } ?: return@executeWithResult failure(buildText { styled(Color.RED) { literal("Module ") - styled(Color.GREY) { + styled(Color.GRAY) { literal("$name ") } literal("not found!") @@ -60,7 +61,7 @@ object ModuleCommand : LambdaCommand() { literal(", ") } clickEvent(suggestCommand("$prefix${input.replace(name, s)}")) { - styled(Color.GREY) { + styled(Color.GRAY) { literal(s) } } @@ -74,7 +75,7 @@ object ModuleCommand : LambdaCommand() { } else { if (enable().value() == module.isEnabled) { this@ModuleCommand.warn(buildText { - styled(Color.GREY) { + styled(Color.GRAY) { literal("$name already ") literal(if (module.isEnabled) "enabled" else "disabled") } @@ -89,7 +90,7 @@ object ModuleCommand : LambdaCommand() { } } this@ModuleCommand.info(buildText { - styled(Color.GREY) { + styled(Color.GRAY) { literal("$name ") } styled(if (module.isEnabled) Color.GREEN else Color.RED) { diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt new file mode 100644 index 000000000..95de12678 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -0,0 +1,12 @@ +package com.lambda.event.events + +import com.lambda.event.Event + +abstract class RenderEvent : Event { + class World : RenderEvent() + + abstract class GUI(val scaleFactor: Double) : RenderEvent() { + class Scaled(scaleFactor: Double) : GUI(scaleFactor) + class Fixed : GUI(1.0) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt new file mode 100644 index 000000000..029fd684e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -0,0 +1,39 @@ +package com.lambda.graphics + +import com.lambda.Lambda.mc +import com.lambda.event.EventFlow +import com.lambda.event.events.RenderEvent +import com.lambda.graphics.gl.GlStateUtils.setupGL +import com.lambda.graphics.gl.Matrices.translate +import com.lambda.module.modules.client.HUD +import net.minecraft.client.util.math.MatrixStack +import org.joml.Matrix4f + +object RenderMain { + var stack = MatrixStack() + + val projectionMatrix = Matrix4f() + val modelViewMatrix: Matrix4f get() = stack.peek().positionMatrix + + @JvmStatic + fun render2D() { + stack = MatrixStack() + + setupGL { + rescale(HUD.scale) + EventFlow.post(RenderEvent.GUI.Scaled(HUD.scale)) + + rescale(1.0) + EventFlow.post(RenderEvent.GUI.Fixed()) + } + } + + private fun rescale(factor: Double) { + val width = mc.window.framebufferWidth.toFloat() + val height = mc.window.framebufferHeight.toFloat() + val scaledWidth = width / factor + val scaledHeight = height / factor + + projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, -1000f, 1000f) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt new file mode 100644 index 000000000..d19a7ec69 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/IRenderContext.kt @@ -0,0 +1,24 @@ +package com.lambda.graphics.buffer.vao + +import java.awt.Color + +interface IRenderContext { + fun vec3(x: Double, y: Double, z: Double): IRenderContext + fun vec2(x: Double, y: Double): IRenderContext + fun color(color: Color): IRenderContext + fun end(): Int + + fun putLine(vertex1: Int, vertex2: Int) + fun putTriangle(vertex1: Int, vertex2: Int, vertex3: Int) + fun putQuad(vertex1: Int, vertex2: Int, vertex3: Int, vertex4: Int) + + fun render() + fun upload() + fun clear() + + fun grow(amount: Int) + + fun use(block: IRenderContext.() -> Unit) { + block() + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt new file mode 100644 index 000000000..087bbf814 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -0,0 +1,202 @@ +package com.lambda.graphics.buffer.vao + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.gl.MemoryUtils.address +import com.lambda.graphics.gl.MemoryUtils.byteBuffer +import com.lambda.graphics.gl.MemoryUtils.capacity +import com.lambda.graphics.gl.MemoryUtils.color +import com.lambda.graphics.gl.MemoryUtils.copy +import com.lambda.graphics.gl.MemoryUtils.int +import com.lambda.graphics.gl.MemoryUtils.vec2 +import com.lambda.graphics.gl.MemoryUtils.vec3 +import com.lambda.graphics.gl.VaoUtils +import com.lambda.graphics.gl.VaoUtils.bindIndexBuffer +import com.lambda.graphics.gl.VaoUtils.bindVertexArray +import com.lambda.graphics.gl.VaoUtils.bindVertexBuffer +import com.lambda.graphics.gl.VaoUtils.bufferData +import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer +import com.lambda.graphics.gl.VaoUtils.unbindVertexArray +import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer +import com.lambda.threading.runOnGameThread +import com.mojang.blaze3d.systems.RenderSystem.drawElements +import org.lwjgl.opengl.GL30C.* +import java.awt.Color +import java.nio.ByteBuffer + +class VAO( + private val drawMode: VertexMode, + attribGroup: VertexAttrib.Group +) : IRenderContext { + private var vao = 0 + private var vbo = 0 + private var ibo = 0 + + private val objectSize: Int + private var verticesPointerStart = 0L + + private lateinit var vertices: ByteBuffer + private var verticesPointer = 0L + + private lateinit var indices: ByteBuffer + private var indicesPointer = 0L + + private var vertexI = 0 + private var indicesCount = 0 + + // region Initializing + init { + val stride = attribGroup.stride + objectSize = stride * drawMode.indicesCount + + runOnGameThread { + vertices = byteBuffer(objectSize * 256 * 4) + verticesPointerStart = address(vertices) + verticesPointer = verticesPointerStart + + indices = byteBuffer(drawMode.indicesCount * 512 * 4) + indicesPointer = address(indices) + + vao = glGenVertexArrays() + bindVertexArray(vao) + + vbo = glGenBuffers() + bindVertexBuffer(vbo) + + ibo = glGenBuffers() + bindIndexBuffer(ibo) + + var pointer = 0L + attribGroup.attributes.forEachIndexed { index, attrib -> + VaoUtils.enableVertexAttribute(index) + VaoUtils.vertexAttribute(index, attrib.componentCount, attrib.gl, attrib.normalized, stride, pointer) + pointer += attrib.size + } + + unbindVertexArray() + unbindVertexBuffer() + unbindIndexBuffer() + } + } + // endregion + + // region Vertex Attributes + override fun vec3(x: Double, y: Double, z: Double): VAO { + verticesPointer += vec3(verticesPointer, x, y, z) + return this + } + + override fun vec2(x: Double, y: Double): VAO { + verticesPointer += vec2(verticesPointer, x, y) + return this + } + + override fun color(color: Color): VAO { + verticesPointer += color(verticesPointer, color) + return this + } + + override fun end(): Int { + return vertexI++ + } + // endregion + + // region Vertex Objects + override fun putLine(vertex1: Int, vertex2: Int) { + growIndices(2) + val p = indicesPointer + indicesCount * 4L + + int(p + 0, vertex1) + int(p + 4, vertex2) + indicesCount += 2 + } + + override fun putTriangle(vertex1: Int, vertex2: Int, vertex3: Int) { + growIndices(3) + val p = indicesPointer + indicesCount * 4L + + int(p + 0, vertex1) + int(p + 4, vertex2) + int(p + 8, vertex3) + indicesCount += 3 + } + + override fun putQuad(vertex1: Int, vertex2: Int, vertex3: Int, vertex4: Int) { + growIndices(6) + val p = indicesPointer + indicesCount * 4L + + int(p + 0, vertex1) + int(p + 4, vertex2) + int(p + 8, vertex3) + int(p + 12, vertex3) + int(p + 16, vertex4) + int(p + 20, vertex1) + indicesCount += 6 + } + // endregion + + // region Memory + override fun grow(amount: Int) { + val cap = vertices.capacity + if ((vertexI + amount + 1) * objectSize < cap) return + + val offset = verticesPointer - verticesPointerStart + var newSize = cap * 2 + if (newSize % objectSize != 0) newSize += newSize % objectSize + val newVertices = byteBuffer(newSize) + + val from = address(vertices) + val to = address(newVertices) + copy(from, to, offset) + + vertices = newVertices + verticesPointerStart = address(vertices) + verticesPointer = verticesPointerStart + offset + } + + private fun growIndices(amount: Int) { + val cap = indices.capacity + if ((indicesCount + amount) * 4 < cap) return + + var newSize = cap * 2 + if (newSize % drawMode.indicesCount != 0) newSize += newSize % (drawMode.indicesCount * 4) + val newIndices = byteBuffer(newSize) + + val from = address(indices) + val to = address(newIndices) + copy(from, to, indicesCount * 4L) + + indices = newIndices + indicesPointer = address(indices) + } + // endregion + override fun render() { + if (indicesCount <= 0) return + bindVertexArray(vao) + drawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT) + unbindVertexArray() + } + + override fun upload() { + // Buffer is empty + if (indicesCount <= 0) return + + // Uploading + val vboData = vertices.limit((verticesPointer - verticesPointerStart).toInt()) + val iboData = indices.limit(indicesCount * 4) + + bindVertexBuffer(vbo) + bufferData(GL_ARRAY_BUFFER, vboData, GL_DYNAMIC_DRAW) + unbindVertexBuffer() + + bindIndexBuffer(ibo) + bufferData(GL_ELEMENT_ARRAY_BUFFER, iboData, GL_DYNAMIC_DRAW) + unbindIndexBuffer() + } + + override fun clear() { + verticesPointer = verticesPointerStart + vertexI = 0 + indicesCount = 0 + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt new file mode 100644 index 000000000..154f64a9d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt @@ -0,0 +1,18 @@ +package com.lambda.graphics.buffer.vao.vertex + +import com.lambda.graphics.gl.GLObject +import org.lwjgl.opengl.GL11C.* + +enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normalized: Boolean, override val gl: Int) : GLObject { + Vec2(2, 4, false, GL_FLOAT), + Vec3(3, 4, false, GL_FLOAT), + Color(4, 1, true, GL_UNSIGNED_BYTE); + + val size = componentCount * componentSize + + enum class Group(vararg val attributes: VertexAttrib) { + POS2_COLOR(Vec2, Color); + + val stride = attributes.sumOf { attribute -> attribute.size } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexMode.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexMode.kt new file mode 100644 index 000000000..8ced5baa3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexMode.kt @@ -0,0 +1,9 @@ +package com.lambda.graphics.buffer.vao.vertex + +import com.lambda.graphics.gl.GLObject +import org.lwjgl.opengl.GL11C.* + +enum class VertexMode(val indicesCount: Int, override val gl: Int) : GLObject { + LINES(2, GL_LINES), + TRIANGLES(3, GL_TRIANGLES) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GLObject.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GLObject.kt new file mode 100644 index 000000000..3046a03ff --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GLObject.kt @@ -0,0 +1,5 @@ +package com.lambda.graphics.gl + +interface GLObject { + val gl: Int +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt new file mode 100644 index 000000000..fc47bd82b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -0,0 +1,69 @@ +package com.lambda.graphics.gl + +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.systems.RenderSystem.depthMask +import org.lwjgl.opengl.GL30C + +@Suppress("NOTHING_TO_INLINE") +object GlStateUtils { + private var depthTestState = true + private var depthMaskState = true + private var blendState = false + private var cullState = true + + fun setupGL(block: () -> Unit) { + val savedDepthTest = depthTestState + val savedDepthMask = depthMaskState + val savedBlend = blendState + val savedCull = cullState + + depthTest(false) + depthMask(false) + blend(true) + cull(false) + lineSmooth(true) + + block() + + depthTest(savedDepthTest) + depthMask(savedDepthMask) + blend(savedBlend) + cull(savedCull) + lineSmooth(false) + } + + @JvmStatic + fun capSet(id: Int, flag: Boolean) { + val field = when (id) { + GL30C.GL_DEPTH_TEST -> ::depthTestState + GL30C.GL_DEPTH -> ::depthMaskState + GL30C.GL_BLEND -> ::blendState + GL30C.GL_CULL_FACE -> ::cullState + else -> return + } + + field.set(flag) + } + + private inline fun blend(flag: Boolean) { + if (flag) { + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + } else RenderSystem.disableBlend() + } + + private inline fun cull(flag: Boolean) { + if (flag) RenderSystem.enableCull() + else RenderSystem.disableCull() + } + + private inline fun depthTest(flag: Boolean) { + if (flag) RenderSystem.enableDepthTest() + else RenderSystem.disableDepthTest() + } + + private inline fun lineSmooth(flag: Boolean) { + if (flag) GL30C.glEnable(GL30C.GL_LINE_SMOOTH) + else GL30C.glDisable(GL30C.GL_LINE_SMOOTH) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt new file mode 100644 index 000000000..7b548b3d8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -0,0 +1,29 @@ +package com.lambda.graphics.gl + +import com.lambda.graphics.RenderMain + +object Matrices { + private val stack get() = RenderMain.stack + private val matrix get() = RenderMain.modelViewMatrix + + fun pushMatrix() { + stack.push() + } + + fun popMatrix() { + stack.push() + } + + fun translate(x: Double, y: Double, z: Double = 0.0) { + matrix.translate(x.toFloat(), y.toFloat(), z.toFloat()) + } + + fun scale(x: Double, y: Double, z: Double = 1.0) { + matrix.scale(x.toFloat(), y.toFloat(), z.toFloat()) + } + + fun scale(value: Double) { + val valueFloat = value.toFloat() + matrix.scale(valueFloat, valueFloat, valueFloat) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt new file mode 100644 index 000000000..c340b48fc --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt @@ -0,0 +1,61 @@ +package com.lambda.graphics.gl + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import org.lwjgl.BufferUtils +import org.lwjgl.system.MemoryUtil +import java.awt.Color +import java.nio.Buffer +import java.nio.ByteBuffer + +object MemoryUtils { + private val vec2Size = VertexAttrib.Vec2.size + private val vec3Size = VertexAttrib.Vec3.size + private val colorSize = VertexAttrib.Color.size + + fun vec2(address: Long, x: Double, y: Double): Int { + float(address + 0, x) + float(address + 4, y) + return vec2Size + } + + fun vec3(address: Long, x: Double, y: Double, z: Double): Int { + float(address + 0, x) + float(address + 4, y) + float(address + 8, z) + return vec3Size + } + + fun color(address: Long, color: Color): Int { + byte(address + 0, color.red .toByte()) + byte(address + 1, color.green.toByte()) + byte(address + 2, color.blue .toByte()) + byte(address + 3, color.alpha.toByte()) + return colorSize + } + + private fun byte(address: Long, value: Byte) { + MemoryUtil.memPutByte(address, value) + } + + fun int(address: Long, value: Int) { + MemoryUtil.memPutInt(address, value) + } + + fun float(address: Long, value: Double) { + MemoryUtil.memPutFloat(address, value.toFloat()) + } + + fun address(buffer: Buffer): Long { + return MemoryUtil.memAddress0(buffer) + } + + fun copy(from: Long, to: Long, bytes: Long) { + MemoryUtil.memCopy(from, to, bytes) + } + + fun byteBuffer(cap: Int): ByteBuffer { + return BufferUtils.createByteBuffer(cap) + } + + val Buffer.capacity get() = capacity() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt new file mode 100644 index 000000000..4aa7d91ce --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/VaoUtils.kt @@ -0,0 +1,44 @@ +package com.lambda.graphics.gl + +import com.mojang.blaze3d.platform.GlStateManager +import net.minecraft.client.render.BufferRenderer +import org.lwjgl.opengl.GL30C.* +import java.nio.ByteBuffer + +@Suppress("NOTHING_TO_INLINE") +object VaoUtils { + @JvmField var lastIbo = 0 + private var prevIbo = 0 + + fun bindVertexArray(vao: Int) { + glBindVertexArray(vao) + BufferRenderer.currentVertexBuffer = null + } + + fun bindVertexBuffer(vbo: Int) = + glBindBuffer(GL_ARRAY_BUFFER, vbo) + + fun bindIndexBuffer(ibo: Int) { + if (ibo != 0) prevIbo = lastIbo + val targetIbo = if (ibo != 0) ibo else prevIbo + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, targetIbo) + } + + fun unbindVertexArray() = + bindVertexArray(0) + + fun unbindVertexBuffer() = + bindVertexBuffer(0) + + fun unbindIndexBuffer() = + bindIndexBuffer(0) + + inline fun enableVertexAttribute(i: Int) = + glEnableVertexAttribArray(i) + + inline fun vertexAttribute(index: Int, size: Int, type: Int, normalized: Boolean, stride: Int, pointer: Long) = + glVertexAttribPointer(index, size, type, normalized, stride, pointer) + + inline fun bufferData(target: Int, data: ByteBuffer, usage: Int) = + GlStateManager._glBufferData(target, data, usage) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt new file mode 100644 index 000000000..650137896 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -0,0 +1,62 @@ +package com.lambda.graphics.shader + +import com.lambda.graphics.RenderMain +import com.lambda.graphics.shader.ShaderUtils.createShaderProgram +import com.lambda.graphics.shader.ShaderUtils.loadShader +import com.lambda.graphics.shader.ShaderUtils.uniformMatrix +import com.lambda.util.LambdaResource +import com.lambda.util.math.Vec2d +import it.unimi.dsi.fastutil.objects.Object2IntMap +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap +import org.joml.Matrix4f +import org.lwjgl.opengl.GL20C.* +import java.awt.Color + +class Shader(fragmentPath: String, vertexPath: String) { + private val uniformCache: Object2IntMap = Object2IntOpenHashMap() + private val id = createShaderProgram( + loadShader(ShaderType.VERTEX_SHADER, LambdaResource("shaders/vertex/$vertexPath.vert")), + loadShader(ShaderType.FRAGMENT_SHADER, LambdaResource("shaders/fragment/$fragmentPath.frag")) + ) + + constructor(path: String) : this(path, path) + + fun use() { + glUseProgram(id) + set("u_Projection", RenderMain.projectionMatrix) + set("u_ModelView", RenderMain.modelViewMatrix) + } + + private fun loc(name: String) = + if (uniformCache.containsKey(name)) + uniformCache.getInt(name) + else + glGetUniformLocation(id, name).let { location -> + uniformCache.put(name, location) + location + } + + operator fun set(name: String, v: Boolean) = + glUniform1i(loc(name), if (v) 1 else 0) + + operator fun set(name: String, v: Int) = + glUniform1i(loc(name), v) + + operator fun set(name: String, v: Double) = + glUniform1f(loc(name), v.toFloat()) + + operator fun set(name: String, vec: Vec2d) = + glUniform2f(loc(name), vec.x.toFloat(), vec.y.toFloat()) + + operator fun set(name: String, color: Color) = + glUniform4f( + loc(name), + color.red / 255f, + color.green / 255f, + color.blue / 255f, + color.alpha / 255f + ) + + operator fun set(name: String, mat: Matrix4f) = + uniformMatrix(loc(name), mat) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderType.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderType.kt new file mode 100644 index 000000000..afa0cfe2b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderType.kt @@ -0,0 +1,10 @@ +package com.lambda.graphics.shader + +import com.lambda.graphics.gl.GLObject +import org.lwjgl.opengl.GL20C.GL_FRAGMENT_SHADER +import org.lwjgl.opengl.GL20C.GL_VERTEX_SHADER + +enum class ShaderType(override val gl: Int) : GLObject { + FRAGMENT_SHADER(GL_FRAGMENT_SHADER), + VERTEX_SHADER(GL_VERTEX_SHADER) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt new file mode 100644 index 000000000..61166045f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/shader/ShaderUtils.kt @@ -0,0 +1,82 @@ +package com.lambda.graphics.shader + +import com.google.common.collect.ImmutableList +import com.lambda.util.LambdaResource +import com.mojang.blaze3d.platform.GlStateManager +import org.apache.commons.io.IOUtils +import org.joml.Matrix4f +import org.lwjgl.BufferUtils +import org.lwjgl.opengl.GL30C.* + +object ShaderUtils { + private val matrixBuffer = BufferUtils.createFloatBuffer(4 * 4) + private const val shaderInfoLogLength = 512 + + fun loadShader(type: ShaderType, resource: LambdaResource): Int { + // Create new shader object + val shader = glCreateShader(type.gl) + + // Attach source code and compile it + val text = IOUtils.toString(resource.stream, Charsets.UTF_8) + GlStateManager.glShaderSource(shader, ImmutableList.of(text)) + val error = compileShader(shader) + + // Handle error + error?.let { err -> + val builder = StringBuilder() + .append("Failed to compile ${type.name} shader").appendLine() + .append("Path: ${resource.path}").appendLine() + .append("Compiler output:").appendLine() + .append(err) + + throw RuntimeException(builder.toString()) + } + + return shader + } + + fun createShaderProgram(vert: Int, frag: Int): Int { + // Create new shader program + val program = glCreateProgram() + val error = linkProgram(program, vert, frag) + + // Handle error + error?.let { err -> + val builder = StringBuilder() + .append("Failed to link shader program").appendLine() + .append("Output:").appendLine() + .append(err) + + throw RuntimeException(builder.toString()) + } + + glDeleteShader(vert) + glDeleteShader(frag) + + return program + } + + private fun compileShader(shader: Int): String? { + glCompileShader(shader) + val status = glGetShaderi(shader, GL_COMPILE_STATUS) + + return if (status != GL_FALSE) null + else glGetShaderInfoLog(shader, shaderInfoLogLength) + } + + private fun linkProgram(program: Int, vertShader: Int, fragShader: Int): String? { + glAttachShader(program, vertShader) + glAttachShader(program, fragShader) + glLinkProgram(program) + + val status = glGetProgrami(program, GL_LINK_STATUS) + + return if (status != GL_FALSE) null + else glGetProgramInfoLog(program, shaderInfoLogLength) + } + + fun uniformMatrix(location: Int, v: Matrix4f) { + v.get(matrixBuffer) + glUniformMatrix4fv(location, false, matrixBuffer) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt new file mode 100644 index 000000000..5abf7a399 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt @@ -0,0 +1,13 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag + +object HUD : Module( + name = "HUD", + description = "Visual behaviour configuration", + defaultTags = setOf(ModuleTag.MOVEMENT) +) { + val scale by setting("Scale", 2.0, 0.5..4.0, 0.01, description = "UI Scale factor") + val chatSaturation by setting("Chat Font Saturation", 0.7, 0.0..1.0, 0.05, description = "How colorful your chat is") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/RocketExtend.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/RocketExtend.kt index 281e63ac6..c4960141e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/RocketExtend.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/RocketExtend.kt @@ -28,7 +28,7 @@ object RocketExtend : Module( val rockets = event.packet.entityIds.map(world::getEntityById) .filter { it is FireworkRocketEntity && it.shooter == player } .mapNotNull { it as? FireworkRocketEntity } - .also { event.packet.entityIds.removeAll(it.map(FireworkRocketEntity::getId)) } + .also { event.packet.entityIds.removeAll(it.map(FireworkRocketEntity::getId).toSet()) } extendedRockets.addAll(rockets) } } diff --git a/common/src/main/kotlin/com/lambda/util/Communication.kt b/common/src/main/kotlin/com/lambda/util/Communication.kt index 1b0273f24..8a1327a40 100644 --- a/common/src/main/kotlin/com/lambda/util/Communication.kt +++ b/common/src/main/kotlin/com/lambda/util/Communication.kt @@ -9,6 +9,7 @@ import com.lambda.util.StringUtils.capitalize import com.lambda.util.text.* import net.minecraft.client.toast.SystemToast import net.minecraft.text.Text +import java.awt.Color object Communication { fun Any.debug(message: String) = log(LogLevel.DEBUG.text(message), LogLevel.DEBUG) @@ -56,7 +57,7 @@ object Communication { private fun Any.source( logLevel: LogLevel, - color: Color = Color.GREY + color: Color = Color.GRAY ) = buildText { text(logLevel.prefix()) diff --git a/common/src/main/kotlin/com/lambda/util/LambdaResource.kt b/common/src/main/kotlin/com/lambda/util/LambdaResource.kt new file mode 100644 index 000000000..f462bd3ad --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/LambdaResource.kt @@ -0,0 +1,6 @@ +package com.lambda.util + +class LambdaResource(val path: String) { + val stream get() = + javaClass.getResourceAsStream("/assets/lambda/$path") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/math/Color.kt b/common/src/main/kotlin/com/lambda/util/math/Color.kt new file mode 100644 index 000000000..15cdcffeb --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/math/Color.kt @@ -0,0 +1,19 @@ +package com.lambda.util.math + +import java.awt.Color + +val Color.hsb get() = Color.RGBtoHSB(red, green, blue, null) + .map(Float::toDouble) + .toDoubleArray() + +fun DoubleArray.readHSB() = + Color.getHSBColor(this[0].toFloat(), this[1].toFloat(), this[2].toFloat()) + +val Color.hue get() = hsb[0] +val Color.saturation get() = hsb[1] +val Color.brightness get() = hsb[2] + +val Color.r get() = red / 255f +val Color.g get() = green / 255f +val Color.b get() = blue / 255f +val Color.a get() = alpha / 255f \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt new file mode 100644 index 000000000..31f85bf9b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/math/Vec2d.kt @@ -0,0 +1,32 @@ +package com.lambda.util.math + +data class Vec2d(val x: Double, val y: Double) { + constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble()) + constructor(x: Float, y: Float) : this(x.toDouble(), y.toDouble()) + + operator fun plus(vec2d: Vec2d) = plus(vec2d.x, vec2d.y) + operator fun plus(add: Double) = plus(add, add) + fun plus(x: Double, y: Double) = Vec2d(this.x + x, this.y + y) + + operator fun minus(vec2d: Vec2d) = minus(vec2d.x, vec2d.y) + operator fun minus(sub: Double) = minus(sub, sub) + fun minus(x: Double, y: Double) = plus(-x, -y) + + operator fun times(vec2d: Vec2d) = times(vec2d.x, vec2d.y) + operator fun times(multiplier: Double) = times(multiplier, multiplier) + fun times(x: Double, y: Double) = Vec2d(this.x * x, this.y * y) + + operator fun div(vec2d: Vec2d) = div(vec2d.x, vec2d.y) + operator fun div(divider: Double) = div(divider, divider) + fun div(x: Double, y: Double) = Vec2d(this.x / x, this.y / y) + + companion object { + val ZERO = Vec2d(0.0, 0.0) + val ONE = Vec2d(1.0, 1.0) + + val LEFT = Vec2d(-1.0, 0.0) + val RIGHT = Vec2d(1.0, 0.0) + val TOP = Vec2d(0.0, 1.0) + val BOTTOM = Vec2d(0.0, -1.0) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/text/Color.kt b/common/src/main/kotlin/com/lambda/util/text/Color.kt deleted file mode 100644 index 30b47b06c..000000000 --- a/common/src/main/kotlin/com/lambda/util/text/Color.kt +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2023 The Quilt Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.lambda.util.text - -import net.minecraft.block.MapColor -import net.minecraft.text.TextColor -import net.minecraft.util.DyeColor -import net.minecraft.util.Formatting -import kotlin.math.roundToInt - - -/** - * Converts RGB values from floats to a [Color] for use in text. - * Each argument should be within 0 and 1. If a number is outside the range, the function will automatically default it - * to the maximum or minimum value, depending on which is nearer to the provided value. - * - * @param red The red channel of the color - * @param green The green channel of the color - * @param blue The blue channel of the color - * - * @return A [Color] created from the provided RGB Channels - */ -@Suppress("MagicNumber") -fun Color(red: Float, green: Float, blue: Float): Color = Color( - (red.coerceIn(0F, 1F) * 255).roundToInt(), - (green.coerceIn(0F, 1F) * 255).roundToInt(), - (blue.coerceIn(0F, 1F) * 255).roundToInt() -) - -/** - * Converts RGB values from doubles to a [Color] for use in text. - * Each argument should be within 0 and 1. If a number is outside the range, the function will automatically default it - * to the maximum or minimum value, depending on which is nearer to the provided value. - * - * @param red The red channel of the color - * @param green The green channel of the color - * @param blue The blue channel of the color - * - * @return A [Color] created from the provided RGB Channels - */ -@Suppress("MagicNumber") -fun Color(red: Double, green: Double, blue: Double): Color = Color( - (red.coerceIn(0.0, 1.0) * 255).roundToInt(), - (green.coerceIn(0.0, 1.0) * 255).roundToInt(), - (blue.coerceIn(0.0, 1.0) * 255).roundToInt(), -) - -/** - * A color class that can transform RGB values into color codes. - * - * @property value The RGB value to convert to a color code. - */ -@JvmInline -value class Color(val value: Int) { - @Suppress("MagicNumber") - constructor(red: Int, green: Int, blue: Int) : this( - (red.coerceIn(0, 255) shl 16) + - (green.coerceIn(0, 255) shl 8) + - blue.coerceIn(0, 255) - ) - - /** A color of red influenced by [value]. */ - val red: Int get() = value shr 16 and 0xFF - - /** A color of green influenced by [value]. */ - val green: Int get() = value shr 8 and 0xFF - - /** A color of blue influenced by [value]. */ - val blue: Int get() = value and 0xFF - - companion object { - /** Minecraft's native black color. */ - val BLACK: Color = Color(0x000000) - - /** Minecraft's native dark blue color. */ - val DARK_BLUE: Color = Color(0x0000AA) - - /** Minecraft's native dark green color. */ - val DARK_GREEN: Color = Color(0x00AA00) - - /** Minecraft's native dark aqua color. */ - val DARK_AQUA: Color = Color(0x00AAAA) - - /** Minecraft's native dark red color. */ - val DARK_RED: Color = Color(0xAA0000) - - /** Minecraft's native dark purple color. */ - val DARK_PURPLE: Color = Color(0xAA00AA) - - /** Minecraft's native gold color. */ - val GOLD: Color = Color(0xFFAA00) - - /** Minecraft's native grey color. */ - val GREY: Color = Color(0xAAAAAA) - - /** Minecraft's native dark grey color. */ - val DARK_GREY: Color = Color(0x555555) - - /** Minecraft's native blue color. */ - val BLUE: Color = Color(0x5555FF) - - /** Minecraft's native green color. */ - val GREEN: Color = Color(0x55FF55) - - /** Minecraft's native aqua color. */ - val AQUA: Color = Color(0x55FFFF) - - /** Minecraft's native red color. */ - val RED: Color = Color(0xFF5555) - - /** Minecraft's native light purple color. */ - val LIGHT_PURPLE: Color = Color(0xFF55FF) - - /** Minecraft's native yellow color. */ - val YELLOW: Color = Color(0xFFFF55) - - /** Minecraft's native white color. */ - val WHITE: Color = Color(0xFFFFFF) - - /** - * Gets a color from [TextColor] and converts it to [Color]. - * - * @param color The [TextColor] to convert - * @return A [Color] from [TextColor] - */ - fun from(color: TextColor): Color { - return Color(color.rgb) - } - - /** - * Gets a color from [Formatting] and converts it to [Color]. - * If the color value is null, it defaults to black. - * - * @param color The [Formatting] to convert - * @return A [Color] from [Formatting] or black if invalid/null - */ - fun from(color: Formatting): Color { - return Color(color.colorValue?.let(::Color)?.value ?: BLACK.value) - } - - /** - * Gets a color from [MapColor] and converts it to [Color]. - * - * @param color The [MapColor] to convert - * @return A [Color] from [MapColor] - */ - fun from(color: MapColor): Color { - return Color(color.color) - } - - /** - * Gets a color from [DyeColor] and converts it to [Color]. - * - * @param color The [DyeColor] to convert - * @return A [Color] from [DyeColor] - */ - fun from(color: DyeColor): Color { - return Color(color.signColor) - } - } - - /** - * Converts [value] to a [TextColor]. - * - * @return A [TextColor] created from the [value] value - */ - fun toTextColor(): TextColor { - return TextColor.fromRgb(value) - } -} diff --git a/common/src/main/kotlin/com/lambda/util/text/StyleDsl.kt b/common/src/main/kotlin/com/lambda/util/text/StyleDsl.kt index 4d2e811a8..53e227a18 100644 --- a/common/src/main/kotlin/com/lambda/util/text/StyleDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/text/StyleDsl.kt @@ -18,11 +18,9 @@ package com.lambda.util.text -import net.minecraft.text.ClickEvent -import net.minecraft.text.HoverEvent -import net.minecraft.text.MutableText -import net.minecraft.text.Style +import net.minecraft.text.* import net.minecraft.util.Identifier +import java.awt.Color /** * Marks objects as being part the Style Builder DSL. @@ -177,7 +175,7 @@ class StyleBuilder { * @param blue The blue channel of the color */ fun color(red: Double, green: Double, blue: Double) { - this.color = Color(red, green, blue) + color(red.toFloat(), green.toFloat(), blue.toFloat()) } /** @@ -225,7 +223,7 @@ class StyleBuilder { * into this builder. */ fun copyFrom(base: Style) { - color = base.color?.let(Color::from) ?: color + color = base.color?.let { Color(it.rgb) } ?: color bold = base.isBold italic = base.isItalic strikethrough = base.strikethrough @@ -250,7 +248,7 @@ class StyleBuilder { } return Style( - color?.toTextColor(), + color?.let { TextColor.fromRgb(it.rgb) }, bold, italic, underlined, diff --git a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt index ba2a59120..ba6cc18f6 100644 --- a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt @@ -16,8 +16,12 @@ package com.lambda.util.text +import com.lambda.module.modules.client.HUD +import com.lambda.util.math.hsb +import com.lambda.util.math.readHSB import net.minecraft.text.* import net.minecraft.util.Identifier +import java.awt.Color import java.util.* /** @@ -191,7 +195,11 @@ fun TextBuilder.empty() { */ @TextDsl inline fun TextBuilder.color(color: Color?, action: TextBuilder.() -> Unit) { - withProp(color, { this.color }, { this.color = it }, action) + val processedColor = color?.hsb?.apply { + this[1] *= HUD.chatSaturation + }?.readHSB() + + withProp(processedColor, { this.color }, { this.color = it }, action) } /** @@ -320,7 +328,7 @@ fun TextBuilder.styled( @TextDsl fun TextBuilder.styled(style: Style, action: TextBuilder.() -> Unit) { styled( - style.color?.let(Color::from) ?: this.style.color, + style.color?.let { Color(it.rgb) } ?: this.style.color, style.isBold, style.isItalic, style.isUnderlined, diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag b/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag new file mode 100644 index 000000000..d4c6f6047 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag @@ -0,0 +1,8 @@ +#version 330 core + +in vec4 v_Color; +out vec4 color; + +void main() { + color = v_Color; +} diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert b/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert new file mode 100644 index 000000000..f71f1481b --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert @@ -0,0 +1,14 @@ +#version 330 core + +layout (location = 0) in vec4 pos; +layout (location = 1) in vec4 color; + +uniform mat4 u_Projection; +uniform mat4 u_ModelView; + +out vec4 v_Color; + +void main() { + gl_Position = u_Projection * u_ModelView * pos; + v_Color = color; +} diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index e224d5616..e3268362b 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -9,6 +9,10 @@ accessible field net/minecraft/client/world/ClientWorld entityManager Lnet/minec # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; +# Renderer +accessible field com/mojang/blaze3d/systems/RenderSystem$ShapeIndexBuffer id I +accessible field net/minecraft/client/render/BufferRenderer currentVertexBuffer Lnet/minecraft/client/gl/VertexBuffer; + # Text accessible field net/minecraft/text/Style color Lnet/minecraft/text/TextColor; accessible field net/minecraft/text/Style bold Ljava/lang/Boolean; diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index c70e75893..655f4e7cc 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -10,7 +10,10 @@ "KeyboardMixin", "MinecraftClientMixin", "MixinKeyBinding", - "PlayerEntityMixin" + "PlayerEntityMixin", + "render.GlStateManagerMixin", + "render.VertexBufferMixin", + "render.InGameHudMixin" ], "injectors": { "defaultRequire": 1 From 25907a34f2573b1206d3a8bd76ced0cc37fefce0 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Tue, 19 Mar 2024 17:10:51 +0300 Subject: [PATCH 02/46] Rect Renderer --- .../kotlin/com/lambda/graphics/RenderMain.kt | 3 +- .../buffer/vao/vertex/VertexAttrib.kt | 4 +- .../com/lambda/graphics/renderer/IRenderer.kt | 38 +++++++++ .../com/lambda/graphics/renderer/Renderer.kt | 54 ++++++++++++ .../graphics/renderer/entry/IRenderEntry.kt | 32 +++++++ .../graphics/renderer/entry/gui/RectEntry.kt | 83 +++++++++++++++++++ .../renderer/impl/AbstractGuiRenderer.kt | 13 +++ .../renderer/impl/gui/RectRenderer.kt | 23 +++++ .../com/lambda/graphics/shader/Shader.kt | 11 ++- .../com/lambda/threading/MainThreadInit.kt | 17 ++++ .../kotlin/com/lambda/util/text/TextDsl.kt | 10 ++- .../shaders/fragment/renderer/rect.frag | 27 ++++++ .../lambda/shaders/vertex/renderer/rect.vert | 24 ++++++ 13 files changed, 329 insertions(+), 10 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt create mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag create mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 029fd684e..8ceb9b4a8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -18,6 +18,7 @@ object RenderMain { @JvmStatic fun render2D() { stack = MatrixStack() + translate(0.0, 0.0, -3000.0) setupGL { rescale(HUD.scale) @@ -34,6 +35,6 @@ object RenderMain { val scaledWidth = width / factor val scaledHeight = height / factor - projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, -1000f, 1000f) + projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, 1000f, 21000f) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt index 154f64a9d..1dbd7a96b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt @@ -11,7 +11,9 @@ enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normali val size = componentCount * componentSize enum class Group(vararg val attributes: VertexAttrib) { - POS2_COLOR(Vec2, Color); + POS2_COLOR(Vec2, Color), + + RECT(Vec2, Vec2, Vec3, Color); val stride = attributes.sumOf { attribute -> attribute.size } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt new file mode 100644 index 000000000..2a78ecca6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt @@ -0,0 +1,38 @@ +package com.lambda.graphics.renderer + +import com.lambda.graphics.renderer.entry.IRenderEntry + +interface IRenderer > { + val asRenderer get() = this // Downcast + + /** + * Registers new render entry + * + * Forces the renderer to rebuild itself on the next update tick + */ + fun build(block: T.() -> Unit): T + + /** + * Removes given entry from the render set + * + * Forces the renderer to rebuild itself on the next update tick + */ + fun remove(entry: T): T + + /** + * Performs a draw call + */ + fun render() + + /** + * Ticks all render entries and rebuilds VAO if needed + * + * For DynamicESP renderers should be called from tick event + */ + fun update() + + /** + * Clears the render set + */ + fun clear() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt new file mode 100644 index 000000000..38eac9aa7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -0,0 +1,54 @@ +package com.lambda.graphics.renderer + +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.renderer.entry.IRenderEntry +import kotlinx.coroutines.* +import kotlin.properties.Delegates + +abstract class Renderer > : IRenderer { + private val entrySet = mutableSetOf() + private var rebuild = true + + abstract val vao: VAO + protected abstract fun newEntry(block: T.() -> Unit): T + + override fun build(block: T.() -> Unit) = + newEntry(block).process(entrySet::add) + + override fun remove(entry: T) = + entry.process(entrySet::remove) + + override fun render() { + if (rebuild) { + rebuild = false + + vao.clear() + entrySet.forEach { it.build(vao) } + vao.upload() + } + + vao.render() + } + + override fun update() { + entrySet.forEach(IRenderEntry::update) + } + + override fun clear() { + entrySet.clear() + vao.clear() + } + + private fun T.process(action: T.() -> Unit): T { + this.apply(action) + rebuild = true + + return this + } + + fun field(initValue: T) = + Delegates.observable(initValue) { _, prev: T, curr: T -> + if (prev == curr) return@observable + rebuild = true + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt new file mode 100644 index 000000000..9d191e6ea --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt @@ -0,0 +1,32 @@ +package com.lambda.graphics.renderer.entry + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.renderer.IRenderer + +interface IRenderEntry > { + val owner: IRenderer + val updateBlock: T.() -> Unit + + /** + * Builds data for rendering + */ + fun build(ctx: IRenderContext) + + /** + * Updates this render entry + */ + fun update() { + @Suppress("UNCHECKED_CAST") + updateBlock(this as T) + } + + /** + * Destroys this render entry + * + * Calling this function has the same result as calling renderer.remove(myEntry) + */ + fun destroy() { + @Suppress("UNCHECKED_CAST") + owner.remove(this as T) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt new file mode 100644 index 000000000..9a43263e5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt @@ -0,0 +1,83 @@ +package com.lambda.graphics.renderer.entry.gui + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.renderer.entry.IRenderEntry +import com.lambda.graphics.renderer.impl.gui.RectRenderer +import com.lambda.util.math.Vec2d +import java.awt.Color +import kotlin.math.min + +class RectEntry( + override val owner: RectRenderer, + override val updateBlock: IRectEntry.() -> Unit +) : IRectEntry { + override var position by owner.field(Vec2d.ZERO to Vec2d.ZERO) + + override var roundRadius by owner.field(0.0) + + private var leftTop by owner.field(Color.WHITE!!) + private var rightTop by owner.field(Color.WHITE!!) + private var rightBottom by owner.field(Color.WHITE!!) + private var leftBottom by owner.field(Color.WHITE!!) + + override fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) { + this.leftTop = leftTop + this.rightTop = rightTop + this.rightBottom = rightBottom + this.leftBottom = leftBottom + } + + override fun build(ctx: IRenderContext) = ctx.use { + if (leftTop .alpha < MIN_ALPHA && + rightTop .alpha < MIN_ALPHA && + rightBottom.alpha < MIN_ALPHA && + leftBottom .alpha < MIN_ALPHA + ) return@use + + val pos1 = position.first + val pos2 = position.second + + val size = pos2 - pos1 + if (size.x < MIN_SIZE || size.y < MIN_SIZE) return@use + + val halfSize = size * 0.5 + val minSize = min(halfSize.x, halfSize.y) + + var round = roundRadius + round = min(round, minSize) + + val p1 = pos1 - 0.75 + val p2 = pos2 + 0.75 + + grow(4) + + putQuad( + vec2(p1.x, p1.y).vec2(0.0, 0.0).vec3(size.x, size.y, round).color(leftTop).end(), + vec2(p1.x, p2.y).vec2(0.0, 1.0).vec3(size.x, size.y, round).color(leftBottom).end(), + vec2(p2.x, p2.y).vec2(1.0, 1.0).vec3(size.x, size.y, round).color(rightBottom).end(), + vec2(p2.x, p1.y).vec2(1.0, 0.0).vec3(size.x, size.y, round).color(rightTop).end() + ) + } + + companion object { + private const val MIN_ALPHA = 5 + private const val MIN_SIZE = 0.5 + } +} + +interface IRectEntry : IRenderEntry { + var position: Pair + + val size get() = position.second - position.first + val center get() = position.first + size * 0.5 + + var roundRadius: Double + + fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) + + fun color(color: Color) = color(color, color, color, color) + + fun colorH(left: Color, right: Color) = color(left, right, right, left) + + fun colorV(top: Color, bottom: Color) = color(top, top, bottom, bottom) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt new file mode 100644 index 000000000..188932f6b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt @@ -0,0 +1,13 @@ +package com.lambda.graphics.renderer.impl + +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode +import com.lambda.graphics.renderer.Renderer +import com.lambda.graphics.renderer.entry.IRenderEntry + +abstract class AbstractGuiRenderer > ( + vertexType: VertexAttrib.Group +) : Renderer() { + override val vao = VAO(VertexMode.TRIANGLES, vertexType) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt new file mode 100644 index 000000000..d30e7d77d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt @@ -0,0 +1,23 @@ +package com.lambda.graphics.renderer.impl.gui + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.renderer.entry.gui.IRectEntry +import com.lambda.graphics.renderer.entry.gui.RectEntry +import com.lambda.graphics.renderer.impl.AbstractGuiRenderer +import com.lambda.graphics.shader.Shader + +class RectRenderer : AbstractGuiRenderer( + VertexAttrib.Group.RECT +) { + override fun render() { + shader.use() + super.render() + } + + override fun newEntry(block: IRectEntry.() -> Unit) = + RectEntry(this, block) + + companion object { + private val shader = Shader("renderer/rect") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt index 650137896..3b49a70aa 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -4,6 +4,7 @@ import com.lambda.graphics.RenderMain import com.lambda.graphics.shader.ShaderUtils.createShaderProgram import com.lambda.graphics.shader.ShaderUtils.loadShader import com.lambda.graphics.shader.ShaderUtils.uniformMatrix +import com.lambda.threading.mainThread import com.lambda.util.LambdaResource import com.lambda.util.math.Vec2d import it.unimi.dsi.fastutil.objects.Object2IntMap @@ -14,10 +15,12 @@ import java.awt.Color class Shader(fragmentPath: String, vertexPath: String) { private val uniformCache: Object2IntMap = Object2IntOpenHashMap() - private val id = createShaderProgram( - loadShader(ShaderType.VERTEX_SHADER, LambdaResource("shaders/vertex/$vertexPath.vert")), - loadShader(ShaderType.FRAGMENT_SHADER, LambdaResource("shaders/fragment/$fragmentPath.frag")) - ) + private val id by mainThread { + createShaderProgram( + loadShader(ShaderType.VERTEX_SHADER, LambdaResource("shaders/vertex/$vertexPath.vert")), + loadShader(ShaderType.FRAGMENT_SHADER, LambdaResource("shaders/fragment/$fragmentPath.frag")) + ) + } constructor(path: String) : this(path, path) diff --git a/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt new file mode 100644 index 000000000..074f873ec --- /dev/null +++ b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt @@ -0,0 +1,17 @@ +package com.lambda.threading + +import kotlin.reflect.KProperty + +class MainThreadInit (private val initializer: () -> T) { + private lateinit var value: T + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = value + + init { + runOnGameThread { + value = initializer() + } + } +} + +fun mainThread(initializer: () -> T) = MainThreadInit(initializer) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt index ba6cc18f6..6762065c3 100644 --- a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt @@ -195,11 +195,13 @@ fun TextBuilder.empty() { */ @TextDsl inline fun TextBuilder.color(color: Color?, action: TextBuilder.() -> Unit) { - val processedColor = color?.hsb?.apply { - this[1] *= HUD.chatSaturation - }?.readHSB() + val transform: (Color?) -> Color? = { + it?.hsb?.apply { + this[1] *= HUD.chatSaturation + }?.readHSB() + } - withProp(processedColor, { this.color }, { this.color = it }, action) + withProp(color, { transform(this.color) }, { this.color = it }, action) } /** diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag new file mode 100644 index 000000000..d0bdf3952 --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag @@ -0,0 +1,27 @@ +#version 330 core + +in vec2 v_TexCoord; +in vec4 v_Color; +in vec2 v_Size; +in float v_RoundRadius; + +out vec4 color; + +#define SMOOTHING 0.5 + +void main() { + vec2 halfSize = v_Size * 0.5; + + float radius = v_RoundRadius; + + vec2 smoothVec = vec2(SMOOTHING); + vec2 coord = mix(-smoothVec, v_Size + smoothVec, v_TexCoord); + + vec2 center = halfSize - coord; + float distance = length(max(abs(center) - halfSize + radius, 0.0)) - radius; + + float alpha = 1.0 - smoothstep(-SMOOTHING, SMOOTHING, distance); + alpha = clamp(alpha, 0.0, 1.0); + + color = v_Color * vec4(1.0, 1.0, 1.0, alpha); +} \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert new file mode 100644 index 000000000..6a6a65fab --- /dev/null +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert @@ -0,0 +1,24 @@ +#version 330 core + +layout (location = 0) in vec4 pos; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec3 data; +layout (location = 3) in vec4 color; + +uniform mat4 u_Projection; +uniform mat4 u_ModelView; + +out vec2 v_TexCoord; +out vec4 v_Color; +out vec2 v_Size; +out float v_RoundRadius; + +void main() { + gl_Position = u_Projection * u_ModelView * pos; + + v_TexCoord = uv; + v_Color = color; + + v_Size = data.xy; + v_RoundRadius = data.z; +} \ No newline at end of file From b081b6c077bf137976b7244990ba98a916bbd5ba Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 20 Mar 2024 18:50:39 +0300 Subject: [PATCH 03/46] Rect Renderer refactor --- .../lambda/graphics/renderer/{entry => }/IRenderEntry.kt | 3 +-- .../main/kotlin/com/lambda/graphics/renderer/IRenderer.kt | 2 -- .../main/kotlin/com/lambda/graphics/renderer/Renderer.kt | 1 - .../renderer/{impl => gui}/AbstractGuiRenderer.kt | 4 ++-- .../renderer/{entry/gui => gui/rect}/RectEntry.kt | 5 ++--- .../renderer/{impl/gui => gui/rect}/RectRenderer.kt | 6 ++---- .../main/kotlin/com/lambda/module/modules/client/HUD.kt | 1 - common/src/main/kotlin/com/lambda/util/text/TextDsl.kt | 8 +------- 8 files changed, 8 insertions(+), 22 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/renderer/{entry => }/IRenderEntry.kt (87%) rename common/src/main/kotlin/com/lambda/graphics/renderer/{impl => gui}/AbstractGuiRenderer.kt (79%) rename common/src/main/kotlin/com/lambda/graphics/renderer/{entry/gui => gui/rect}/RectEntry.kt (94%) rename common/src/main/kotlin/com/lambda/graphics/renderer/{impl/gui => gui/rect}/RectRenderer.kt (66%) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderEntry.kt similarity index 87% rename from common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/IRenderEntry.kt index 9d191e6ea..3c0297986 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/IRenderEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderEntry.kt @@ -1,7 +1,6 @@ -package com.lambda.graphics.renderer.entry +package com.lambda.graphics.renderer import com.lambda.graphics.buffer.vao.IRenderContext -import com.lambda.graphics.renderer.IRenderer interface IRenderEntry > { val owner: IRenderer diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt index 2a78ecca6..2e1f014df 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt @@ -1,7 +1,5 @@ package com.lambda.graphics.renderer -import com.lambda.graphics.renderer.entry.IRenderEntry - interface IRenderer > { val asRenderer get() = this // Downcast diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt index 38eac9aa7..e82350c64 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -1,7 +1,6 @@ package com.lambda.graphics.renderer import com.lambda.graphics.buffer.vao.VAO -import com.lambda.graphics.renderer.entry.IRenderEntry import kotlinx.coroutines.* import kotlin.properties.Delegates diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt similarity index 79% rename from common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt index 188932f6b..a074c5801 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/AbstractGuiRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/AbstractGuiRenderer.kt @@ -1,10 +1,10 @@ -package com.lambda.graphics.renderer.impl +package com.lambda.graphics.renderer.gui import com.lambda.graphics.buffer.vao.VAO import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.Renderer -import com.lambda.graphics.renderer.entry.IRenderEntry +import com.lambda.graphics.renderer.IRenderEntry abstract class AbstractGuiRenderer > ( vertexType: VertexAttrib.Group diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt index 9a43263e5..98ac8b79b 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/entry/gui/RectEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt @@ -1,8 +1,7 @@ -package com.lambda.graphics.renderer.entry.gui +package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.vao.IRenderContext -import com.lambda.graphics.renderer.entry.IRenderEntry -import com.lambda.graphics.renderer.impl.gui.RectRenderer +import com.lambda.graphics.renderer.IRenderEntry import com.lambda.util.math.Vec2d import java.awt.Color import kotlin.math.min diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt similarity index 66% rename from common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt rename to common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt index d30e7d77d..10f44231d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/impl/gui/RectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt @@ -1,9 +1,7 @@ -package com.lambda.graphics.renderer.impl.gui +package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.vao.vertex.VertexAttrib -import com.lambda.graphics.renderer.entry.gui.IRectEntry -import com.lambda.graphics.renderer.entry.gui.RectEntry -import com.lambda.graphics.renderer.impl.AbstractGuiRenderer +import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader class RectRenderer : AbstractGuiRenderer( diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt index 5abf7a399..fe86f5b38 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt @@ -9,5 +9,4 @@ object HUD : Module( defaultTags = setOf(ModuleTag.MOVEMENT) ) { val scale by setting("Scale", 2.0, 0.5..4.0, 0.01, description = "UI Scale factor") - val chatSaturation by setting("Chat Font Saturation", 0.7, 0.0..1.0, 0.05, description = "How colorful your chat is") } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt index 6762065c3..d3fb6d67c 100644 --- a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt @@ -195,13 +195,7 @@ fun TextBuilder.empty() { */ @TextDsl inline fun TextBuilder.color(color: Color?, action: TextBuilder.() -> Unit) { - val transform: (Color?) -> Color? = { - it?.hsb?.apply { - this[1] *= HUD.chatSaturation - }?.readHSB() - } - - withProp(color, { transform(this.color) }, { this.color = it }, action) + withProp(color, { this.color }, { this.color = it }, action) } /** From 967fdaf7e0e64d1c9e05364d1304b84e9706618a Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 20 Mar 2024 23:33:21 +0300 Subject: [PATCH 04/46] Fixed merge conflicts --- .../kotlin/com/lambda/command/commands/ModuleCommand.kt | 2 +- common/src/main/resources/lambda.mixins.common.json | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt index 70b897769..eab191232 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/ModuleCommand.kt @@ -34,7 +34,7 @@ object ModuleCommand : LambdaCommand() { } this@ModuleCommand.info(buildText { - styled(Color.GREY) { + styled(Color.GRAY) { literal("Enabled Modules: ") } enabled.forEachIndexed { index, module -> diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index d8d90833f..4ed37c2bc 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -7,10 +7,13 @@ "ChatInputSuggestorMixin", "ChatScreenMixin", "ClientConnectionMixin", + "KeyBindingMixin", "KeyboardMixin", "MinecraftClientMixin", - "KeyBindingMixin", - "PlayerEntityMixin" + "PlayerEntityMixin", + "render.GlStateManagerMixin", + "render.InGameHudMixin", + "render.VertexBufferMixin" ], "injectors": { "defaultRequire": 1 From 2436692e9bed5deb715035e35743f0de7bb9fc82 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 23 Mar 2024 23:28:38 +0300 Subject: [PATCH 05/46] Font Renderer --- common/src/main/kotlin/com/lambda/Loader.kt | 1 + .../buffer/vao/vertex/VertexAttrib.kt | 3 +- .../com/lambda/graphics/renderer/IRenderer.kt | 2 - .../graphics/renderer/gui/font/FontEntry.kt | 115 +++++++++++++++++ .../renderer/gui/font/FontRenderer.kt | 22 ++++ .../graphics/renderer/gui/font/LambdaFont.kt | 21 ++++ .../renderer/gui/font/glyph/CharInfo.kt | 9 ++ .../renderer/gui/font/glyph/FontGlyphs.kt | 90 +++++++++++++ .../renderer/gui/font/glyph/FontTexture.kt | 31 +++++ .../com/lambda/graphics/shader/Shader.kt | 1 + .../com/lambda/graphics/texture/Texture.kt | 29 +++++ .../lambda/graphics/texture/TextureUtils.kt | 119 ++++++++++++++++++ .../module/modules/client/FontSettings.kt | 19 +++ .../com/lambda/module/modules/client/HUD.kt | 39 +++++- .../assets/lambda/fonts/FiraSans-Bold.ttf | Bin 0 -> 438028 bytes .../assets/lambda/fonts/FiraSans-Regular.ttf | Bin 0 -> 403924 bytes .../shaders/fragment/renderer/font.frag | 13 ++ .../lambda/shaders/vertex/renderer/font.vert | 18 +++ .../src/main/resources/lambda.accesswidener | 1 + 19 files changed, 528 insertions(+), 5 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt create mode 100644 common/src/main/resources/assets/lambda/fonts/FiraSans-Bold.ttf create mode 100644 common/src/main/resources/assets/lambda/fonts/FiraSans-Regular.ttf create mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag create mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/renderer/font.vert diff --git a/common/src/main/kotlin/com/lambda/Loader.kt b/common/src/main/kotlin/com/lambda/Loader.kt index fb804ca99..03aa08718 100644 --- a/common/src/main/kotlin/com/lambda/Loader.kt +++ b/common/src/main/kotlin/com/lambda/Loader.kt @@ -2,6 +2,7 @@ package com.lambda import com.lambda.Lambda.LOG import com.lambda.command.CommandManager +import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.module.ModuleRegistry import kotlin.system.measureTimeMillis diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt index 1dbd7a96b..8085a20c4 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/vertex/VertexAttrib.kt @@ -11,8 +11,7 @@ enum class VertexAttrib(val componentCount: Int, componentSize: Int, val normali val size = componentCount * componentSize enum class Group(vararg val attributes: VertexAttrib) { - POS2_COLOR(Vec2, Color), - + FONT(Vec2, Vec2, Color), RECT(Vec2, Vec2, Vec3, Color); val stride = attributes.sumOf { attribute -> attribute.size } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt index 2e1f014df..9b8ccf05d 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt @@ -24,8 +24,6 @@ interface IRenderer > { /** * Ticks all render entries and rebuilds VAO if needed - * - * For DynamicESP renderers should be called from tick event */ fun update() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt new file mode 100644 index 000000000..6da4e54e2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -0,0 +1,115 @@ +package com.lambda.graphics.renderer.gui.font + +import com.lambda.graphics.buffer.vao.IRenderContext +import com.lambda.graphics.renderer.IRenderEntry +import com.lambda.graphics.renderer.gui.font.glyph.CharInfo +import com.lambda.module.modules.client.FontSettings +import com.lambda.util.math.Vec2d +import java.awt.Color + +class FontEntry( + override val owner: FontRenderer, + override val updateBlock: IFontEntry.() -> Unit, + private val font: LambdaFont +) : IFontEntry { + override var text by owner.field("") + override var position by owner.field(Vec2d.ZERO) + + override var color by owner.field(Color.WHITE!!) + override var scale by owner.field(1.0) + override var shadow by owner.field(true) + + private var shadowSetting by owner.field(true) + private var shadowBrightness by owner.field(0.0) + private var shadowShift by owner.field(0.0) + private var baselineOffset by owner.field(0.0) + private var gap by owner.field(0.0) + private val actualScale get() = scale * 0.12 + + override fun getWidth(text: String): Double { + var width = 0.0 + + text.forEach { char -> + val glyph = font[char] ?: return@forEach + width += glyph.size.x + gap + } + + return width * actualScale + } + + override val height: Double + get() = font.glyphs.fontHeight * actualScale * 0.7 + + override fun build(ctx: IRenderContext) = ctx.use { + val scaledShadowShift = shadowShift * actualScale + val scaledGap = gap * actualScale + val shadowColor = getShadowColor(color) + + var posX = baselineOffset * actualScale + val posY = height * -0.5 + + text.toCharArray().forEach { char -> + val charInfo = font[char] ?: return@forEach + val scaledSize = charInfo.size * actualScale + + val pos1 = Vec2d(posX, posY) + val pos2 = pos1 + scaledSize + + if (shadow && FontSettings.shadow) { + val shadowPos1 = pos1 + scaledShadowShift + val shadowPos2 = shadowPos1 + scaledSize + putCharQuad(shadowPos1, shadowPos2, shadowColor, charInfo) + } + + putCharQuad(pos1, pos2, color, charInfo) + + posX += scaledSize.x + scaledGap + } + } + + override fun update() { + shadowSetting = FontSettings.shadow + shadowBrightness = FontSettings.shadowBrightness + shadowShift = FontSettings.shadowShift * 4.0 + baselineOffset = FontSettings.baselineOffset * 2.0f - 20f + gap = FontSettings.gapSetting * 0.5f - 0.8f + + super.update() + } + + private fun IRenderContext.putCharQuad(pos1: Vec2d, pos2: Vec2d, color: Color, ci: CharInfo) { + val x = position.x + val y = position.y + + putQuad( + vec2(pos1.x + x, pos1.y + y).vec2(ci.uv1.x, ci.uv1.y).color(color).end(), + vec2(pos1.x + x, pos2.y + y).vec2(ci.uv1.x, ci.uv2.y).color(color).end(), + vec2(pos2.x + x, pos2.y + y).vec2(ci.uv2.x, ci.uv2.y).color(color).end(), + vec2(pos2.x + x, pos1.y + y).vec2(ci.uv2.x, ci.uv1.y).color(color).end() + ) + } + + private fun getShadowColor(color: Color) = Color( + (color.red * shadowBrightness).toInt(), + (color.green * shadowBrightness).toInt(), + (color.blue * shadowBrightness).toInt(), + color.alpha + ) +} + +interface IFontEntry : IRenderEntry { + var text: String + var position: Vec2d + + var color: Color + var scale: Double + var shadow: Boolean + + fun getWidth(text: String): Double + + val width get() = getWidth(text) + val height: Double + + val widthVec get() = Vec2d(width, 0.0) + val heightVec get() = Vec2d(0.0, height) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt new file mode 100644 index 000000000..ebac6a3f3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -0,0 +1,22 @@ +package com.lambda.graphics.renderer.gui.font + +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.renderer.gui.AbstractGuiRenderer +import com.lambda.graphics.shader.Shader + +class FontRenderer(private val font: LambdaFont = LambdaFont.FiraSansRegular) : AbstractGuiRenderer( + VertexAttrib.Group.FONT +) { + override fun render() { + shader.use() + font.bind() + super.render() + } + + override fun newEntry(block: IFontEntry.() -> Unit) = + FontEntry(this, block, font) + + companion object { + private val shader = Shader("renderer/font") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt new file mode 100644 index 000000000..baa826fb7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt @@ -0,0 +1,21 @@ +package com.lambda.graphics.renderer.gui.font + +import com.lambda.graphics.renderer.gui.font.glyph.FontGlyphs +import com.lambda.util.LambdaResource +import java.awt.Font + +enum class LambdaFont(fontName: String) { + FiraSansRegular("FiraSans-Regular"), + FiraSansBold("FiraSans-Bold"); + + val glyphs = FontGlyphs(getFont(fontName)) + + fun bind() = glyphs.bind() + operator fun get(char: Char) = glyphs.getChar(char) + + private fun getFont(name: String): Font { + val resource = LambdaResource("fonts/$name.ttf") + val stream = resource.stream ?: throw IllegalStateException("Failed to locate font $name") + return Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(64.0f) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt new file mode 100644 index 000000000..ab5791219 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/CharInfo.kt @@ -0,0 +1,9 @@ +package com.lambda.graphics.renderer.gui.font.glyph + +import com.lambda.util.math.Vec2d + +class CharInfo( + val size: Vec2d, + val uv1: Vec2d, + val uv2: Vec2d +) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt new file mode 100644 index 000000000..201671853 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -0,0 +1,90 @@ +package com.lambda.graphics.renderer.gui.font.glyph + +import com.lambda.graphics.texture.TextureUtils.getCharImage +import com.lambda.graphics.texture.TextureUtils.rescale +import com.lambda.util.math.Vec2d +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap +import java.awt.Color +import java.awt.Font +import java.awt.Graphics2D +import java.awt.image.BufferedImage +import kotlin.math.max +import kotlin.math.pow + +class FontGlyphs(font: Font) { + private val charMap = Int2ObjectOpenHashMap() + private val fontTexture: FontTexture + + var fontHeight = 0.0; private set + + init { + val image = BufferedImage(TEXTURE_SIZE, TEXTURE_SIZE, BufferedImage.TYPE_INT_ARGB) + + val graphics = image.graphics as Graphics2D + graphics.background = Color(0, 0, 0, 0) + + var x = SPACE + var y = SPACE + var rowHeight = 0 + + charList.forEach { char -> + val charImage = getCharImage(font, char) + + val fullWidth = charImage.width + SPACE + val fullHeight = charImage.height + SPACE + + rowHeight = max(rowHeight, fullHeight) + + if (x + fullWidth >= TEXTURE_SIZE) { + y += rowHeight + x = SPACE + rowHeight = 0 + } + + check(y + fullHeight <= TEXTURE_SIZE) { "Can't load font glyphs. Too small texture size" } + + graphics.drawImage(charImage, x, y, null) + + val size = Vec2d(charImage.width, charImage.height) + val uv1 = Vec2d(x, y) * ONE_TEXEL_SIZE + val uv2 = Vec2d(x, y).plus(size) * ONE_TEXEL_SIZE + + charMap[char.code] = CharInfo(size, uv1, uv2) + fontHeight = max(fontHeight, size.y) + + x += charImage.width + SPACE + } + + val lodImages = (0..LOD_LEVELS).map { level -> + if (level == 0) return@map image + val size = TEXTURE_SIZE / (2.0.pow(level).toInt()) + rescale(image, size) + } + + fontTexture = FontTexture(lodImages) + } + + fun bind() = fontTexture.bind() + + fun getChar(char: Char): CharInfo? = + charMap[char.code] + + companion object { + private const val TEXTURE_SIZE = 2048 + private const val SPACE = 2 + private const val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE + private const val LOD_LEVELS = 4 + + private val latin = ('a'..'z').toList() + private val cyrillic = ('а'..'я').toList() + + private val german = "üÜöÖäÄß".toCharArray().toList() + private val ukrainian = "ґҐїЇєЄ".toCharArray().toList() + + private val special = ((32..47) + (58..64) + (91..96) + (123..126)).map(::Char) + + private val charList = special + + latin + cyrillic + + german + ukrainian + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt new file mode 100644 index 000000000..987c9fe80 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt @@ -0,0 +1,31 @@ +package com.lambda.graphics.renderer.gui.font.glyph + +import com.lambda.graphics.texture.Texture +import com.lambda.graphics.texture.TextureUtils.setupLOD +import com.lambda.graphics.texture.TextureUtils.upload +import com.lambda.module.modules.client.FontSettings +import org.lwjgl.opengl.GL14.* +import java.awt.image.BufferedImage + +class FontTexture(private val textures: List) : Texture() { + private var lastLod: Float? = null + + override fun init() { + setupLOD(textures.size) + + textures.forEachIndexed { level: Int, image: BufferedImage -> + upload(image, level) + } + } + + override fun bind() { + super.bind() + + val targetLod = FontSettings.lodBias.toFloat() + + if (lastLod == targetLod) return + lastLod = targetLod + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, targetLod) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt index 3b49a70aa..b0ec5c513 100644 --- a/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt +++ b/common/src/main/kotlin/com/lambda/graphics/shader/Shader.kt @@ -15,6 +15,7 @@ import java.awt.Color class Shader(fragmentPath: String, vertexPath: String) { private val uniformCache: Object2IntMap = Object2IntOpenHashMap() + private val id by mainThread { createShaderProgram( loadShader(ShaderType.VERTEX_SHADER, LambdaResource("shaders/vertex/$vertexPath.vert")), diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt new file mode 100644 index 000000000..0ff1b10f2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt @@ -0,0 +1,29 @@ +package com.lambda.graphics.texture + +import com.lambda.graphics.texture.TextureUtils.bindTexture +import com.lambda.threading.mainThread +import org.lwjgl.opengl.GL13.glGenTextures + +abstract class Texture { + private val id by mainThread { + glGenTextures().apply { + bindTexture(this) + init() + } + } + + protected abstract fun init() + + open fun bind() = bindTexture(id) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Texture + + return id == other.id + } + + override fun hashCode() = id +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt new file mode 100644 index 000000000..eeab5b7dd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt @@ -0,0 +1,119 @@ +package com.lambda.graphics.texture + +import com.mojang.blaze3d.platform.GlConst +import com.mojang.blaze3d.platform.GlStateManager +import com.mojang.blaze3d.systems.RenderSystem +import net.minecraft.client.texture.NativeImage +import org.lwjgl.BufferUtils +import org.lwjgl.opengl.GL12C.* +import org.lwjgl.opengl.GL13 +import java.awt.Color +import java.awt.Font +import java.awt.RenderingHints +import java.awt.image.BufferedImage +import java.io.ByteArrayOutputStream +import java.nio.IntBuffer +import javax.imageio.ImageIO +import kotlin.math.roundToInt +import kotlin.math.sqrt + +object TextureUtils { + fun bindTexture(id: Int, slot: Int = 0) { + RenderSystem.activeTexture(GL13.GL_TEXTURE0 + slot) + RenderSystem.bindTexture(id) + } + + fun upload(bufferedImage: BufferedImage, lod: Int) { + val width = bufferedImage.width + val height = bufferedImage.height + + glTexImage2D(GL_TEXTURE_2D, lod, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as IntBuffer?) + + setupTexture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR) + readImage(bufferedImage, lod) + GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_WRAP_S, GlConst.GL_CLAMP_TO_EDGE) + GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_WRAP_T, GlConst.GL_CLAMP_TO_EDGE) + } + + fun setupLOD(levels: Int) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, levels) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels) + } + + fun setupTexture(minFilter: Int, magFilter: Int) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0) + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0) + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0) + glPixelStorei(GL_UNPACK_ALIGNMENT, 4) + } + + private fun readImage(bufferedImage: BufferedImage, lod: Int) { + val stream = ByteArrayOutputStream() + ImageIO.write(bufferedImage, "png", stream) + + val bytes = stream.toByteArray() + val buffer = BufferUtils + .createByteBuffer(bytes.size) + .put(bytes) + .flip() + + val image = NativeImage.read(buffer) + val width = bufferedImage.width + val height = bufferedImage.height + + glTexSubImage2D(GL_TEXTURE_2D, lod, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image.pointer) + buffer.clear() + } + + fun getCharImage(font: Font, char: Char): BufferedImage { + val tempGraphics2D = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).createGraphics() + tempGraphics2D.font = font + val fontMetrics = tempGraphics2D.fontMetrics + tempGraphics2D.dispose() + + val charWidth = if (fontMetrics.charWidth(char) > 0) fontMetrics.charWidth(char) else 8 + val charHeight = if (fontMetrics.height > 0) fontMetrics.height else font.size + + val charImage = BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB) + val graphics2D = charImage.createGraphics() + + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + graphics2D.font = font + graphics2D.color = Color.WHITE + graphics2D.drawString(char.toString(), 0, fontMetrics.ascent) + graphics2D.dispose() + + return charImage + } + + fun rescale(imageIn: BufferedImage, targetSize: Int): BufferedImage { + var image = imageIn + + var size = image.width + val divisor = sqrt((size / targetSize).toDouble()) + + do { + if (size > targetSize) { + size = (size / divisor).roundToInt().coerceAtLeast(targetSize) + } + + val tempImage = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB) + val graphics2D = tempImage.createGraphics() + + graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR) + graphics2D.drawImage(image, 0, 0, size, size, null) + graphics2D.dispose() + + image = tempImage + } while (size != targetSize) + + return image + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt new file mode 100644 index 000000000..dd9f35e28 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt @@ -0,0 +1,19 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag + +object FontSettings : Module( + name = "FontSettings", + description = "Font renderer configuration", + defaultTags = setOf(ModuleTag.CLIENT) +) { + val shadow by setting("Shadow", true) + val shadowBrightness by setting("Shadow Brightness", 0.35, 0.0..0.5, 0.01, visibility = { shadow }) + val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05, visibility = { shadow }) + val gapSetting by setting("Gap", 1.5, -10.0..10.0, 0.5) + val baselineOffset by setting("Vertical Offset", 0.0, -10.0..10.0, 0.5) + private val lodBiasSetting by setting("Smoothing", -1.0, -10.0..10.0, 0.5) + + val lodBias get() = lodBiasSetting * 0.25f - 0.5f +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt index fe86f5b38..a4c9f032a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt @@ -1,12 +1,49 @@ package com.lambda.module.modules.client +import com.lambda.event.events.KeyPressEvent +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.gui.font.FontRenderer +import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.KeyCode +import com.lambda.util.math.Vec2d +import java.awt.Color object HUD : Module( name = "HUD", description = "Visual behaviour configuration", - defaultTags = setOf(ModuleTag.MOVEMENT) + defaultTags = setOf(ModuleTag.CLIENT) ) { val scale by setting("Scale", 2.0, 0.5..4.0, 0.01, description = "UI Scale factor") + + private var y = 30.0 + + init { + val renderer = FontRenderer(LambdaFont.FiraSansRegular).asRenderer + + listener(alwaysListen = true) { + if (it.key != KeyCode.K.key) return@listener + + val yLevel = y + + renderer.build { + position = Vec2d(30.0, yLevel) + text = "I Love Lambda <3" + color = Color(130, 130, 250) + } + + y += 30 + } + + listener(alwaysListen = true) { + renderer.update() + } + + listener(alwaysListen = true) { + renderer.render() + } + } } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/fonts/FiraSans-Bold.ttf b/common/src/main/resources/assets/lambda/fonts/FiraSans-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..95e16602406c4cba92fb24e6aea2560bf10a0f5e GIT binary patch literal 438028 zcmeFa4R};n)i%8Mnct+8LVhNb$t0wdQW`=CA%qYDgp^W5M5IV5B2tPJDMdu2v?5Z9 z6p>OyL_|s{MM@D7v0_E!L8OR?lm`(hQl!{YM2Zv<5i9fE>)z+=GYRRR`d*Lk`#m$) zUUSdA*IIk+-*aYWpEF7;rLrJ`s<_YX@4F4}vR4LNj+Xj8` z_VNoOUsK9?R_V?sZoB=%^>+@Pv03S=rAqam^TFFYR@`;uNLQsFDu(^2yYIVe(yw1! zTZZ>DmHJS>yQfYmzV4CUzfs{Qk0SrT*h%-?_e#!h&#CaLjY?hfwR`UR^d#^tkbe-b z(R(I5F!ocGKg?75z-|@H96#>kcZ~@>eqxsjy;O<(s&OE~PUidZegfXN8F$~5X&?XQ z^EcuBLZxm_PWa^Acg5z^4p-sudb}^b@2+W+u33KVpH#TxUg#H3yz9P?fAZcxzlir| zlnQs7^vO?8IsduNTUEHKol^IHYtrP8PkP@c`Zy}Qp&#^rh?Y^R8In>Xm7}gzLFcg2 z&Wp}b0`S-g+7H0y!Hmx-Cu2s&4B*eh-hutYz_T+R0iK&N7x;@AUj&|)F%S4l z84G|P%XkdF7G`_}cyY#8!7s`98t~GLrNCd$_&V^ijAg*z$oK~E@{Hxck7uj^egg4n z#P=lRs*F`&p2~O%cr_w+5b+wwr!$@gvo>QbN_i$@9hh&SCK@%_0QsGajbOf;@m=8W zWqcoUbH-*cKR`V->iJ{HZ5i9ZY|q#Z{F98I0Po1y0sK?6f|K!j#-AZCWc&rp#f-lK zU&^=y{I^U;Ihmo%3`jiC3(^mnx-xh`1^e_JFhnKpeqizhReI0lkKe0$PPl8zL^TNG z8IM$ougz6bm83CRU8f3F`!vSEs4G&Xs!nD0?)&~?HL&;aTZ`3*TY3*ER^x9O3_0!n zy@wa8IUo4ofMT_H&;aPH8vMZz6swIxL}%NO!C)||SYkl6RIOA8_gzqBsbW>;z6+^r zb-n87z6+}y)mmYO(JrEL)eWkX`!1?ts*S2}-({#gb))L+zROf`)mC8^(C!+QuWnLZ z+;`1XLbX#>?z?MMfx20BRn>P-`t&3{m~t59NXjvk<0&UmPNkeqIg4^GB|?_TE!%xunuk$=gOr-DR>b4iPr7kZ>3TAe@(zx((|#hm6y0Y0XTIDP6P5yxuv+ync3%3S&Xl+lQ$ziqJd8sBi$UyV@Ck*9gP^ z3`e6qtjEAP3jMLSCqO4f7c=R3=*DL=Q8p7_tZ|YLjGa;kla^Oe^Y(&{!Hqy z%1@m`d;Cv6GEL!#SfL&Y@tsS(f!D*r3)A#N^!-P}cRnv3Qg5IYq)pzS6oght*ZDt% z8*mLf zS@|9xXp8iAc#qcqq%eZ@ty2N$;B$=)ta1)`4jB9oz}}6 zsf~i6`x1H{zK~x8Z5au#L3<0{XUcoe;=R3Mg-a{TvrV}(g0qpHQJR!^LKsHll_+bf zVK6dTXPLb+iyK7*YokdIVuME}*I%i(l$e8aOFpDDgQP4$w9fHaFZbe-u2BL|k z#dJniIJcclZ<&ZQy?*p{#g$==a=_PsZ*4lI6*NHvNUPWFr$@lV@!y^n@jsBP`y>Hsij8n`hmxmYcVd^05=k=Rk zD~?c4@oisGs<+~^EO*YE*_G+eFmvXCvoODoTrH$$IhsuoD4+e~k2|gjGFPScJH7b$ zZZ8;b7R~^EL*{eC`7il+Zie>|wcS<_oIVoXwu1g_VcWTVXI2jSOShKu4)47|jMZ{p zo_Y0pu_4>&ot<~SXV)6%3-Uk@rj2UDy&LJzCwf7QXe*vwG>*%gk$wcM)z!5(?faUH zKF=51+J=Kw-dzoSUtZ}}>0N+^)^Ba#m3EbeZLy|3rLks2+8&e~H`hJVU*)?2fe5T^ zgN(O)^Dgym4G<5Gsu=s`h{Yr9TRMN&8D11-2ES{wf0H8S`ma()dB)*+^v|nMbJTVp zj(aDNuqps!?USPxwu+lgc3Mo4*X#OTC%0;HUAuR*)XDzOta%|xDe zjMry!6I(MZ6l{#*U?cVY%LC1o_P4wmT4y>&)kQn~Bi+aL1VE&40ys`4ws>pVFFq zNL{dM?ZfINtEH@jhq>a&7?mT4f8@xdhODu&R!U3A8Jw)I?xzY;`nocnHD{_TAJt9cw9O-B7z(V1Gvw}x-kPJTTbp5^|lvA(Un z^u6i-w;2WLeZ}5-8{NjGHL%U}KkLAc{cL1y#t3rfnXC!!`IF6efxl{@EoFr`qbh(7 zqbBZ3*VrQcz2DA;mG^JeC+T+o5!m^DP`-J8q340I_{FdaksnKKzzX+k)EwUgp=C1h z?cH@?WH#da2eb~3{>YW}`Y67IKZ4f6Q9N}JIR~IqiTvN;y?j@HIQ3(E1N0WH{@&_u zdhxy4q-!&xcUT!j-}I)@=KJ4)lhs;!)jMYK+XB(yv(Ygxy;OQ|tsC?2{k@6p-Pyg9 z?mOi-S<1VZ4Ps|!Wutj*`84OLBjyc`vrVplX{v-6Q%$#bqp}*?z1@2`8)zzD#`@K~ zoDmo{{d*fi2)l>D}W{%&Z2qYXbfnJ60N z;q#N1x#<3boIC5({(Jy{u=YJt!3KKJ#+PZxwV&1 z$>!mda-TIR*}`fZDH4^`{J@z+sh^2h!xJPKwhbh%kr$vD|)rb zb+Vz>yM5=D<+Vv8v&QSZx8|m|m6$XYk&Hw0@MeWqKaX$t>PFI#{HNd1oAd23&#T(J zumJm9umcc#7PcUE`3?=|l}Lkd9Fa4peHax**mK2iL+l9or%QjghN(Y@CR+I&RxO6tTTZ)`I*Zv}C`kHP5UZ{>KEPvYn-Vx|Ayx) zHv69deVecu)CBhql+*(KaSm#Bhed}l){t)rE~b8e)p|z7^m9IFc`K{@ z7IoE{DjuK#UQ8SM^WQ;(-)g7FA$EArwv{U2!M0}sb zlKBgU>j@;rcnOb718%-luTcxxi1j{FK|&YUzz8QvXQD-s3r0S{Xr`V(sGEP2>l|fN23+w>SDdM*3S`7@GtYh{JLs(oN@k1 zIpzojBqi}3`oulDV3e8P1^HW_H}-ws?+uw*f>NaC&3PrqfOz>=e7hd$RmL&PN&7_i zPx{tR#%2EDs)!`6FKUOYk1AD-s>Au)0Pfevb!*tQ|6ZiC@~C(`yzRv@{N8;Ne9I16 z{Dy#MjtWV?^J(_;%Wp5T*2{Wi_OD1oz0E`R6nGDQZ|VI8<&k(bXCSf)iC6Z78EtH} zG(`jdh4V+OB}XxO+~0$7{;T-u-YX(~6*gRxYq6&;vqMHPK4~ zMgzWePD8z>-lf;49ctA5Y9;ij>=Bbswq{?1eD=j&GiY+Of@SZ5_0FMWEjQ!9Kk9jB zT+(jl9H9UfC(wp6_npU^$@u$S@$ck#pAFw~#v^@s19!#XI}MDxB3$t(*9~3$Xrc`; z4*7}KRlWwFKiwVZWZas&v3v7L))*6=nTej3oEOOI;4GgbalSnBL;~{!*D`S2bDt;> z*IoP$N_2IQ!g!y0rG3&hnwlY2S3X~$hUO^DR`&ZX-AN9uH{HFnq+;E^}}0bPkQG||T% zbFo7CJQJs1%x=bl)e)tjG)QL|cNUykbmjoA@+=Wwc*>ptnWKNhI5o%if2L(5x_5ds z{h8#QtR$bc;Bhw#wc5xyTcDouy`LQY%z1-+a*$)QjAp5k?4gzYwX!c(81t>LjAmTh z{&k*Foe!UH!z*4D_E=m_UB-3ZDzzf@BK9!KH-sCoiu?grTRTfH%Sbw2|K~v%ee5}U zGv7nX2*jGSlxI|bK6fD#=V{Gg%~2sGH?AULn&DbhTrq6Cr)jVh z)?0t=_|oQBdz$HG>Tac49#J}L4AOX{Nk~({9d}<(0!uoB_tTMPA>qylSqs3O$LnIe z;{E|yxZ^?AN~ASN>s2so8(uddJ%=QCC-knQ`Pt*z?Z=HVa0PbOA-v*>*Q}#RXrnBQ zqpTS4c<4!dr;yHqOR8XY82AE`=(R?|(Fif>tjkKv!7-~$1+sCiQ&y#$Ujsf2T<0c? z8L3ki?j@0p*2-Q2jIy$^er4gzJv)w6gwzJ91gRXU3aJ*U9y)`W{P)Aa_d^3JUDPwH ze^wvn4$2wQnr+7*_>uwns~S%h_qTurRb)$1D@;G)5?Vebl!jE!;iM#jdw zXi{vdi>AkBxoB=|fr}Q$R-)Wxu{GJlV(VRQQ|vhxZL{3Y*dCYLA3JmvIvP9Sa;IWv zU34Mlu3UM_Foh*~;k;%Bry)N#D=%(xvy*x5YL?d~uf(+|&#Q7#ZC<^L`sEFD(U3fM zHOm{4H_C9iSMnyHOss7{lMTw7CY(I8Tjb5on;|G~ULMvkq9u9DU9>81t&29~xvNy( zmb~pQw<~Y2iw@)+cG0oCS6p;D@0^P+=Dlf9+=)kB)I9Fa-`HGfdwJ$_?#}6W zaU3&QdL~}#wM)DrUTrL5O^p)U`+2q=@t*NMu7&M$zo+B<;84~@I?GS+0D$49$f z_FL3MPsjW|KM)^hEb=UhPmDiUSQ5uvPeU`~^W$^CEb?m z@pZ1A9obmT&bTvlXB=~1=Bhv6a*HKbp1H-6E6-R>e6x%C#@&@6z9YWdWllig2lKj^B?efcvMSf*|jf?8?`?_dA{$Ljk%O7b_c3r-^M&ysrpXBCF z&7W>i%(i&`to*rd?gHy&asD!wTbaMcMeFl7xwa$opL4lw`8!>-Cx5>|d0X2DR9kh+E{Y8$Oq4lVEQYmL$p( zwUAgvfUttNsGmVOyAlIk&qET!T{J2&wh1($F&7)0m~1$pX$EE2CAhj^Z1+seb}i;5 zmLwJm&To-eg>siC)+WH2+^n3$27_{sB-}M8u_du9!Br=**Q;e3I*>T*dOntT#YLwR z=UjBryrP^p3mlj8kqxN{olh^u$J;H2r9 zf-_#57Mw4*WODscQgFo^C4TQ`Z%YP^Ue;)@w1W9weA3gV_x(OkW+q!0i)1XBG^nZG z&!3uXZE}6&w|#b9vYp9I*xrv#@?!Q;vMgCsSdy$nvdHhPWStkiKXx0&Y@+wavp+BM zFC_b#8JZkm=aE0(nt8pIXqFu8)^}LaU6B%_l4IPuaaIV2txPt2BB!d!rlT$51 zY4S{)3#XIQ4d>7Of<$tb;rzZ#&h>gVt^Mo_$lH)yU@Vf0lgnJRGP%Y@>yw)d%CTcN zYhv;_!};SXxh=WVaDGcB_ayhbUJfOX8kDyydBUK?#pEeBcR=#2LH@W(UPxXxT%jrq zn_h@rC~Rgpf88j|DvY~&MTKn)@+0v3Qt8xCd>NusRh0cH+fcrrauMY~O8IR}rwS>* zKv_rmRm!O%ul$4fcFG}?cThe~*_LvtNVSl1A{qG&Q>R8a*^nX149Y6XHk2Q7a3{-D z6=fUB8z~DZzd-q6%6mj!IZ51~@(#+IC_hVYUw3^ppd3JXkH{;3BkoTbr@Vu* zE#>2sLq)2moodL9PA5vizaahv`J==~iGNT0d*bQD(}`CSuO@z)_y?{OZ_f}v!`vgp zM+9GaL-$7f&r0iQd=~~eGKG7*>XA}YCr_}GCs_AmZa?A=5Pv{_1pEic$S*b8!8coA z+L0+CE+OXF&}=>R74kQeZy(qS{q8KWJDFVO?xi&4#;FqzJPyt0$v;m`Krk7^1BeF@ zR}*(7?nc}BWWGq|PU3@nd^zaMf@$| z8;EZp?n2y(_%`C78A;}5;x8LX<{9F(#0Q8E5X-OWS|24YAjbF9U^tu9IpGy&nWB|i zL}szHn0i9mR&Ai|_i6Qg;xghg;``bD_Y?n!{Evt^XZ1kh)#O(b_a^R5yqkD8@e<-C z#IF&*CRqJ7uo2NtATxovb%NDD0#}p{JS(LKHggu>+f-O7 zS;w=)eW*`5)$Cyuk+Ge(lp%ncAfMf{Z1SG`I9`0^c*%a`_A2Z?2eveu6nNd~`> zg5j}4b4}KtBl9`tE+Ae&%%hK9Dp;KtUU8jQeHEt+szuk zLFk*=BW6AP3O&bIN{qNIHQQ3>Ve$`?&!o>MoG-(}Wb%{A%kOJi7ZJ}To=ZH6coeaj zU&{rnE8<_B3v__h9F{eQ{7Zomn2)`L3G^mo>YGEI9QwhpIj|Z}ypH@j;%ee*Voh1Z z_*aR(Q^pbXHR06{=wY;;20WKN^)fYi#L;=Q&8D|(;uaix)7gf9r2L)aste3*=llWj z+)SOels8i6W#S-nL&P)4AEWIt+Fr|2@EZ<1l(An*Boe0$<1t6ZXNh?pqIm>YuhGM6 z^zcpEev|my#NQ^apzKSgFL9JOO#Be>L&V=?3~R`&AyY!8g!q2)_Y-GPu4e9P;`PMq zi8Cp0A#)4y&xwCd?9k64%Ag$mu`-BEl8Fa&L2ZdKJ5HBSD zvb2L*EG?#%)9Tao^J!w9mFbqmJS*1kC%%F50ct)#oJF~q%wpn+#1n}(5pNQVqw4$7 zVz-jHl}w8A#lSR#4tgxq)SEApSA&kBPG>>&Vm*?<3wP z7#>lh2VxJ5@_)(B-Jo;~d9j{Is8m{{CVT?iuFnIW6i-I!E265D35iD zCBPq7fxM}BFJ+xXelcFpAmL0Sb_ogRba_D}9O3d>AjP25mdSr#3O^yTeXO6$4U7$k-4LWvv9T^UAvP^GnR+u~vt4dpY@v&m#Fo2gRcx(` zHpHHF5xm5ABcNH+Np_O@s2Byr|2K%4_a& zIeEo-3Bh@7^D6R6iK_GZ;C)Y1kG%fm2IY;&8wz%G-nhJpuI-?_DF($R1XsTk7iBdgQI}>IR>y-CWzwdF%2vy0)A1wz_DC*M51s^AN8*^A6@6anbI) z;|8U*y=|+!lX>T%eFinMEuMEN9t3v<>0~_9;w!!J7hhu|SZ^cP zWFvUaMzGCBu+v7c$420z{qdvmLyYP~{A~Ob(S>{!zbq&}oS&87j3}PpCZFRyza+mZ zzg)Pqv0+C^erk zdNUDCID!(*6Ny9)QE{R)(YBGEwUAL~W=a~Gkf?BH#e_t)L5ZG;{)s;DJ18+UF*-2< zXk21qVv4kV;=#ns#Qek@phbzLiPeb}K=5LHV(}0?rOZU{PXs!7!7H=v~`_k={B~FvcsbV7yma!6dJ=f~j6< z1=GFK3TD~T=GxLMiq#h^aAR3qu*^j(3)Z-3eZeM!p!b~1Z7bO6qCExsU3946sEbY% zaK@lUXA3U4+~uS)C>c(2?iBA!W_e{K<4MjUaBY(1$r8L)C2NzIFY-+GOAd6=kR)c0 zJo7FkN4ebCB<7JklasxAB&Q`YqsYxpVm1*iOfGTJ@+9V#JPZ6tPbAki=Ffd(v`e zEIMz|C5x^YRIsNgXi=s`EiCd@sXawW%eA(soke99xvLaB*I2I3prXFsOfKqMB#-i~SO|odJ zMbj;sWzk%V7Fe{{Lw3DNTMvqsncQTXqLuE7)TU^SMe8lvWYKdTDm>$%qHPxKbWy*e zJ+9||Mf+Vex#*BZM?K_?$>ijs6CPJ|%A&I#^2Q|e+|@QY*&FvjTRpv^%N}Z_EDC$b zn=|RFUaMwq?($Yy7R4Xp;u2qReH)cW zKyEBRZY)4{cOdX!We&V7tB57QwMLt#%nMcDU7E*Wy5{ z!v^JTZgtG%UTJmOMex$<9K2mLo`ual(dtc;TkI4^uR_g>6X0@W#Vjs%bK4e|x~QVK z+C@E!`xw+zq&Z8A`7%eJ1I`bK7D74*)hVsB3JLS(0!=? zKH8eSAFYYaZ!Tuf$pGpMA|ECW6XTnA#BcVWaPNxwwOYN&+~(9w-*aMijF{ix)=GK>LZF5W`^({O6Z}RlZFLK+-bbs>WZ3TxesO^xGa5~AmIPKJh7#@)nUcaU8~R1c z<0749l*=g}qx>r6Jdv6s(=osJ?_=b@Vp;P<>R&Sk)(*d@NmP%Kd5kfz<()5+nMI}> zb!L;9OQsi@d1QjbL5_hSnQO>gL*^PX&y#tc40m2RKO^%qGC!jZe(ypgTgYr7vz^R# zGTVjGUuPW$QQjdk(2_EnGECV_q-G22FNz0uugF4r%cSgYFiUAh%=zUsV=4SC*kK)Y ziaH#b&aKo838TI$y@Sy!dwc#yeu_x_H|m?&!TmqZeCD2+RB>?vx`G1;EA<{Wf>PMvR4a?g_U6!o7Z6Bnu1NRK#uDMzx8;*<$8 zZxTDi=R|7ma@6IV(dDvh!rf;Ob5`UaNBokC`>f_j;r&90I*^tOmHeP>Yx8M9BL9X)iEQq-Sl z`zMxIM&_bus*7aUQ~CkwKfv6-Qva{aP1{SO%`E1=9W_sOp6Gm$?k)neyD3gTTwQVG zk~*R&=sc_C*K@OTX#nvD^&Q}UAX2wsiQM;vU)v?-6J$;h|Aij@!j|bz|NV)*JpfWl zmgwj#@`I=|jm$L0VD@WqH<#J7g#Amhi|NnQ@%Ca#%bR^%HGx}^dztbFl;5O0O1Vv> zZWH(cWC3Lx?h4DNj61KBm+_O&y*eM}Sh|U}uTd_d{5j>fMCvTsMkxD`2{QL5l;5E| zNx563E@QbjQkK!`2Flh_y4o!zs&&-=1f?lGmv{hW2g=3b0lg`5v3O8lq?}7X^8(`M zi&6{d;3{_ASFCi1wm+r(F6Alm<&5D*%0XmWPtH_qMEF}s7xsdy_&w&l%Yac|)1Xrp8 z?OOx4aP_dlmf#EvF*@yINEksS*fmpvBW?-KUrHpuKX|l6$sjjjjh8x=30`| zMp^A~##b@|ua!tONOeekk+4go{a~bFNF$*$n#q5U|50y025CGJc9XQ9iZmT*7SdcK z@wEVHG2(iV$!Wg~?^hzNL0XT5v%8Xs!HklUlJb(O`iu1!iE1sXx2PY{Ko1nJ25Q7Hx1Hn?m6cYysK)bGT4zyTLE6OkUw*a||Em~&LN}@H$U2oAQiwyT1a<`Ea zw3BEL(0vKC_x>`D+beK$T8qf?P;bvPj&!UAEE$Oh_ zwZ*7&(OQd)Kk>4G+_ScfhMu=ri|rQevdDPZ3-1TW9qw?f!z(uTG|@TaqI^g}Z)%=aI|SDD!Q7?htICfm$QAvs@WDKUYwt z@EOJ;?%8P6+^;jBvMphLw63g2_uY`Jn z$}Ot0$j^lpbm|*e^b<}-N%_D=7Feaqhm#wHqtz=GZLrAapaJc%>3Zc8$|r+sXuFDf z(>y)zXbL^7bLI1pW|uE?(GqgY$@#hP;OhCDi;yE|EiDkGtM@F>7M8o+a=U=AjzJ#4 zYJ*V$Wc_t)U4FP@yYgewF6D>IUlCM(y8K-E#qu`|*U9m6J4G#O-YLh;O<1njL%zRG zZJXdqh3iytb-5Bjr)uA#Q%^4{LB^sXXDmFfQy&kdMbO{#*J+T4+*}#YF=-8Y8S3SB z>SNIeL7hgU)%J^Cr*TLV1(o#ynj%Qf|2jQr(M*fxbeiwxF0vfg-A*fDv06BPR?y!% za()j2b=v50oi=yc3Jc+O2-j&h-kF{TSHjWWX&?B5*5Zgo$7yjAxo3#Z!`6%l*!o^N zVV#A}mHLYnL5t8zS8**Y7ZX&G^eie`1GN*b9ZISwV{Ro-T}9uD8X0vJ1IP^q8YZX& zEnG2@+!&zoM3aD~5={r1MKl*^0nuWhWkf51j6WZ(0k>XqOUG1fs(8+#Z5HjcXpf-s z4bW@E(RRPpJ7m#Oi%wW{s^YBcr4dIj7b-5BSURiDVHY*?Y&&OJ6t}2I5TmlucWz^I zODrn4s7g?W;q@0ga~$|cBIsQ2>G`eExu3^%9w?~u5G2^U@r^QE=dp(CJOR0r$qAZf z(F{T5SYJBNwrC!67kV6HSwe2PNTOB9T}!k92xHKV-so)sx1G6y;2p9dx0l?326~6d z9c#e7B3uQ=Q0LQv+UEeBqn`E0=-;e#I$x}e)?ch_F1eM^t4s(=%Pod2_0qVu&@1)y zDl5cGXQ{dISB>1B^w$TdzvRk{uN*{fsMQ+*ZZy3}?l|U71e(IA9t4_6B-|XL`9O;V zRm`niTDd|jh*n#)&Z3RykujP1FvkOpvU4T`C)LU23de9Z+B93K~E(7-$&L zNT4x-+M@Ka(A&ZV$biyLv_LOjyBVc=$xfg&g3+k{J$kftu6^N=zNp4jbsaZq4 zwA?J@#wE9G9K5i!sv>YDNNr@CR#hRDldBbusJ^Nn&_KMWUeyqw;Vf;GMZ%3GHvwoe z(X^@=RkJ;0R`e?2=6PJzLW`DIwA@2!7OR@zjOVrKWmIkO^s1hqxwP)9gu7dgy^#>Zn+@V0DyN>HRLRPb`Q@TFbbt3hQ#Y}L% ztwD3Z&9|Nx(ce<5w*qK2xphDrska$ut8ns7N7vn5_jTRT^&rp@<{k$+)AfAUlU*+X zT@g;sk*b3hWqQc;LUju-*GIlZ6~>i0?hC4piLK97qy4JQ*p=LNl3QJtZeeVzE5V~R zjcs)u_4=mkRS#&$RS))Z)6g(@8R>aRiwf-k&qdYat0z@YbzApAAvq>Po-Yuqx{S8sCBbJlj7MLR9pW6^#h@$5%>$mSlkXj1hFTMt3T z!s8Cv+@ltqvWT9m&!YWM3)jm9>-n;Wx+#mog1R+B%CZ)Ie6;8mM=sVUbM9?gQqG#I zr(26t)?XWoN{BEv%qrEb9A2u#Le8Jf*^;1cwI0{4UXXm|?AFiYIxl%ER5B2HNEqXg zo!=DHZ3t#F#v9OZB$SQPrvZ(%Xo4U)r|CA?a?rR+Zw9%wo^7|;@Vr2{Zu5{93hH#y z<}LwRZoRCco^QJWxzCc5a<>!h0@_P-0KK)|L){L0D2+Qt?iEkZKXP`1ZMV}+Sezq= z7+o)D9Us*=lIwe^iPm4N$*D=yG;b0WBe$(s)RbDi3X6Q(YT>BY(;~_3L(l!K#UQlg z0IN5YXoSrj4Kz+T`NUE)k-1ZV9weG6dPH-8<`XRfT1vD6XdTk(nvIZ~$!+yeWwd5T z)!3TdIGzL5>=RVJtL9+Mk(%Q!chcsbvFN--mn^#CA>Uv3U=v)XaOKm|%kAF6)9W7d zP}SJ(NiQlv#=_&ex3DN?QELySMbOUk*S*X`CRffL%&e7~cdztvySKKeMo{-U^h``z zt$SZ2_EGo2K*Pw51j0%RIUZ?}MN=)BZqY1@<`QA%bYD!g3}~gG%6&j<1j%>xW>(ao zSIt?PpziA>muQnk&k5?jt%2T7a(le!X|dm09AfTKcp67^0?3RjDdQA5soPni3-D6r zp>7jA7)SCe`uXbVW?9$diOUNw;T1B)LXoDd6^i=ySxh=KZ zYw-^61?{!yfJKLijv==(2MhDbmA0=~3t#WF<<8Yg8RDfeC*{7`!!Z%`i1ujiq8!h* zN5Z0Fi`o*2KO23IQkz?0QME-qE$ZW;G~50j*JF^N9zzj@?|Fo96_wyeTf{L$ZlZ8< zu4h`j9DQL>k14R=dX?5&=#L&V#lq)$%mFuFIHE-s`P@=+D;nsnwtDLX)owxVM$6Gw z&eD2pMcV9-FmgMDle4tOxz&9=uE%bmgGl>&9Pv2cOIq~Dk$Vzpa}Pmh1eIRyalXeT ziG}Ejhk6Dr%Jh)0*RzGk^^EmQ)?e(|y2ts(ocQb6t|uZVs_a?gqB?7f8o1m5LEiHbeHrVdzxxeSmo=tG_J9f`QqDOSpq7xRKvgoWuzQqOMdS0%-*h>lO z6-H_%r~^63RS8FgxO)}VV3ruYb_aXa_v$CMjnF{L4Y6pr zMWZYlYtaOYe1DUL>ou)G88fWjY>U{=-G)PNq2-oXM1S&Gve$CUtrFC0Ez$;yp0$Xa zjP_nzgd^H+kqc_DuzTYsp zbx!@ox~OpSyFpz}U81geT`^EwDUGOb*iF(2o{f75Ib$vYKb7`oGx&6~E{MvJIp(*VgC8!G5A=Q8z_U`L4PLYoD!~X;9r94|yDB1$#mC>gId7 zb&D)oYSD_i)i{G+2)#xeZP(Rpbj#RWx79^EtnF@#_E~h$q9cODvlkW0IBs)KdI-5^ zY(0Qn3y(W)bI)6J$wS7ooWImv@!F+#(DU3o)1np@#XRIkW%Nof*I(?NG+uyOThz|; zTzAQ{?OkS3rJ#}$Sk#D~9IblS36kFpdiS+xfS}%kk%n20xw2!S_ekbqjQ1Wds1`HK zJ>LN8J;~#GPxVk*?sVGDO1DVMoy*(>-nf!uvWbd_Q8nT)Z=kcRf!<2te7){_yj*{! zuEB`yy&h>zBeba@*ZaAv^?IWRd+(Goy1nV;$~SzyQ4ZvO%dG)AB%GXu_dY79CnD_$ z9_OJpf|8QnCwkZPJ|#T^bXHK0Idu>AD(`*4T5lemTO``&Fjm(-1>fkWbHI( z!+!e-S3gkNPtL>YhgdY+qEQx&wP=E%`pHPs1j*;$`Wb?{%UGT*sM8h5c@{0SXo*D_ z-FU7-^7YmVM=u)$(aWJK-;9rL*QSBOqq zi*rC6iLzh7oVEN%U8(l0>_Rp71ODH8Dl6W-4|iymdo#c5dn#+}7{xy2EViV3M`YfO z*t<69D`;VJm&eY*NrHoMk-S%KyMSDK@s$8}%Icc}qPZx5`=#lZg0lPjMrwYUjFx-o z;?9?kLzV`^kR^dt8WvdmpScanM5cqTUYe#S0e_%Zj{9ZH;jXXP>UIfk{q zhj%3$&AR`Rbst0jt!Qi$Vc@=Uby0xV2?f3?N* z>}5~ou%~7Uk2|eOPnr8z=d)hs&epBiJNfLLe6}sG+QGd!QKJS|1V$J?uf4-{B+@&) zO3A$k_BD)(*D9$C)XZmJBw1GaorXuVF5a~u($}L|uhH}{iuL*h>ouCTZKdVit7@*$ z=LhKD-1*yFD|tJa+u63aQ|E*9_5pf(j^4~wCcI0i{w+0sOU*8{ZEvJ7O( z_M617P=1rlVrk*@>rK@L)+L{H;gx@S0PA=gBfpJxS)d<-)mP|&_o8-IO6d--07L0A zA8(-k6VxUSp?um6+x=YMQmsU!wmn zvDbdiQh1G*9ze`18Ut10GjJ=T9Zdb(S&I*{^xJ9sLDA74V(x9!=k-;Awv1#j$KIXv zd61G<$GTUtdRJ7)xcUe^jNs@R!BXyI8}4Kq-pN_Ble1<7OB_b#r@}bRIfI6B2JN6u zXU-trhg*M?YuyM&JA!N7Xz5>PGVApbMtGb)kCPd}`SlUbuMt7qOHzMCr24g7KZ?5u zOSE@5zkwVcIL6!wl!!$CjCJIdj=Ge%l=@|?T}Q^iDB72S%lOTG z&8rv>@1d@aN}MYF{_Y*P65hnNTEw<0VqI{B2I5J-Cb)S@*0<(*IAlwf(vtnyl2Ns! z)y=fJnenuc6;D@^sU%~rwA(I>Gm^}0jQPXV;Waw?!@@fgncI)O*^g!2$_O_y!d9%^ zZ9Ed)8kBp}zK?y>k1-5n?<{it%i7hCEpQuq=Fu`GMqO9P3V8ht2hoLB?!mC9m(% z?Kz^N^q)ijF_xIecF1FEUqdT%Kj~|!(}MCdv^vk3%d1Fne{mUaycfGJX0EvgG0v;; zZY0BdqN`^F0Ly`aR%V@Jg?iyke|n;2LS| zzy-*bfrXTEFYuOuD#&Y@JI%p0F?v%#R-|dpS0S4P<`Y9-{Xy;&uFajfTe2Tpa(&1O zd=9+1?|VUD2yn|lE~L44dkbEv)-rH6n5%{_4UBvP zZF{kmdIyd`hpQPppF})cltlX&y*$X@h*Srq*0`fKupUbtbH{!2Ujzjl4{7&$R~My5Dn+Z5cy_NaK1;lcF=W!) zwQTwAY}N0utT%~s*ggT`0Aq`>tzvAe7+WdE`kL#HUu9IkrO)f==QT3s?(Z@BG5>7P zhNTztF7n0HPruXu4P51l15(p31?1X6bC3TdJ(#PaucwD3qxv54_gJF22KtvQ*IbFa zKx%=jaw{pNO&*iT-TxVQOnj=ZQ|If_U*Kg9u3=fvNbS@D={xmf`sCHs%G7iXb01~P z^ZyFeE_(Yp`KO83vbEQ-mD;mjyidP+Gu073|H0fJF#fCV@sI0bUq>Vv%;nYF>es|G z`ELMgSl<*|?`LFQC+cKSosYZc`(YF?twqty8?@;eyq7WzC+ zz8mEySo#E7{fhYW)LBjD3U!_(|3%`b**j}Fik_n9w#2;ahF-{I_6IQ z?HGf(TD^i+9VOTKCFOqR-pDI)7qP_jtJTd_!fiQL+tPnqj@8zj(T{RQ|BQ3_QO@P- zIhP;hT)x7&Z2lXeE!S#uE_V~xYI7BGxr`Hs#~u9thV;=BZtY~`+{l@J6OVdrDL=;u zIa?KvylNNg-ZrpD^m#3^>Dgb9FJ*l@u#V<>bo2iPH?x%Q87Z3h4*^Q*=M$gdtpBOh zLd|8l5Wrt#?qS;UpFPwW8Rv>;&dT&iko~yYSW$l|u|s^GelAN{>hEmTkgS%#GMoM` z(W(pLU-2Ji)OlL*t_%u*iUXrIOy9wzfJ zZFzqM{QxoV$AEvJ600C}LbNjf{$c(B;5zbQw&Zm@mhm4#)HyQEC<9#mIQP{t@vQ#H z5orE7!7y^&I)dX@6GWPZySs`$Sa<_hwc=;tL~?frM^{9UkK!^qcAa}62u zZx#G^4z+_;bI5;;nlItMxV-;ZxP!d8BEN{a<{uvR)Bk?v@@jkSF!vCpxtc!|kgL+e z0r@l(4wOTN#0vQPlvoY4L;gNTdi?3&b&&TtTOf7dyTs5`FUdbnzzY9>0lAX$eUOpB zD#&Im<+{KF9{1=EYt)fB9FZ16T=&x&lS2E@&Y4s#E%}Tg{d=Vw@o}zmY_n>Ak z{u{fDVvF%VL)^P7{Y9cue-%yV{i5m2qvZdMIGY6r zDrNjQ{i(A-q~^bB=$e3xhwiLhEo)cHmaJu*t;H7iDnNTGo{OqK(zY#SN9LyA>7kr$ z%lkLz3VN$xTfHEfig$fb3#CN$WhoKg;Yj3*sPhRK8{pAOV9<|h5y$h=farfi+6uh1 z)sLif_>mU-Ch=p^Ch7mgqE@gL<_;6(jKth=;qQ$30nVn!XlwqrC@c1;;sOq)!!iV4eE3t?nJ(vJ}cO^r;B2kn2wcI>M9n$x_^&MZ zZUW{6awGf` zJah>>4cU(TBFYw&EvfSe+vE{4=HFU6u{Ao8DWpy-j*W|qyqLMI7(*x8X0ZigocmcE zKWX>>Q0HlTo=4#~L|?r@&)14nZ}6CF?pD%<{MXse93i@$l)(+M4m) z%(;9Mzcngnw4J5JoT0S3iS1UxXxp*HN?a?njJiY}b61jSw0(e{AE3|AlbOjn&ZMRp zW9H5!=DsG^Q0H1&@y;bWlkJmD-rRj8#vZ(ebL5ZA&7ikT&XiMZlgpy7-eg<7$r*QA zG|@gH<)O2-Wru;AsU=F*g8fKxG&$-fS*gU=S)>a{@?P*|X>z16QZoS9X?)_>_&l!V zhy{C%&rQ0-<*>GEeD0Mbw#>)(G-9jkk@~r5AYO+clKqI{C`vnp(k`gW8YSswC}F>j z>muDom*{d`rE7J)?xzRpA$qtTrL*){Jps?jdYU}{qkelEmclt@sydYiIUt39(n$@( zx6?Y+HifFCM#v~njiziXV>*?s)1{wN1g2dIpDk0%uE_W1>m@cdmiQ@& zp=C`=SYb?DR^8Db>qc>p%DR-)zTBqLW=L~d)IK!P$&RAyxzOA0-%y4Eq^PGjw z5tL!{KH)&- zLh`e8>h{1^GAqe!guGGCgHx^KxR`oGzTr&`rOqeio8r^}`TUc*Q;vM8o8{YK_`xT} zRD1cP1gln%kI3hQ)G(I*X<4JeTo3tv`CODr$chXPcSE+7&l{;H0+S(!1^UWd6Dynp z!AgCXc&y~&b0*?ZGbA4MT;LwaZ}I4RBycbAcLR4p{!)%r>KIq^WAyMU&!uK?WnWL7 z=XgB$mK=@L5jhexAJZI1eaz*Ua+Fe7qg7l5RE|ohVnoF|i$daaPHLfilb-sLoKHZ4 zX|}34&c^Jq0rLdk#VWPTO)K%b25CLgCZv@veh#nOkai;NK@z+DNQaP)B8mM8q*F-3 zokhCfCb5f|M$~!SG8u*Yh!FFvtlL?TrpBxq*6Ck z;I$g5CsH4z{z!w6P!l}@X*AL}q=`sVkRC)5o0&*+kme&TLRyL>zE>cvMp}m?+(x9$ zNL!J1Aniulhjb9>2-0z+lSpTf&Ldq)d%c3yFNl=MRj|QoEB0@-;-Z{fJyMUsbG)9U zr|Ri?7M^q6=K{Pg#&elosn=*Zp8f9{_g>dHt|01RXD3G25$7k6FH@eNe2MZ^=Xvn= z%6$OUrzrRH_wd&Ov%r`Y$E-C+upT*>{XzMM$zX@z4|RR$PTWeejm7h$>V7;&;=8)J>SLIPi_|Er2+P#R)pGTOnt)Z{muixF zLA{KBr~JJRsV8(qcT+obca--R{UJRL#oUXL64wvnS)`X@Y_!6$cBgKwf1_X0H|XEt ziSG}cJpB5TaEf)g)7rU7cXe)dO7P9VC!9~{Ud|L}imr38*F*Pq_BsEp>z!XYhjB;7 zuLQoL2L#K4W%>ibPQfZYFjyU|)%=> z34U1L9lRs>5&iMtsNg6)J~$>gUf&y>5S*wd2PXw5>CXf|9lT#p4Necv(4P&?49?Uu zf^&mk)SnMN7F?nq3VuEKg#Kdi$>5WEVeqNoYW?Nl)4^})uLQpx+@O~RHwM3_mj#~- zZqX}(r-G;SlfetY3wl-Xa`3W#DijC>^y*NvP&56_P*x~QuL;FN`TFTld8m_qCe%4p zslOGf3RUTEhkAwT^oG!Fq1*Jv(ECI0*WV5OCUjJ93LOg_)6a#Dhfe4phF%H1s<(z- z3!T>6LT5r}^p4PEL3n|4ZFqP1nA1G`w@B2vG4lJ!@0~8uA<+*zRnbSH z^PKAFvgk6WHu_}rNvB7~)QqW4&y3v}yPaMcdo%Vrb(y#cnbSM7YbGxC#n|fR9CNS= zIj=acs(^FKIi*6*>3~+@Kmf<^qQH%T?y6Uy2kwA8G|(4E%aMV8xC8IU0u%Ag>Zri< zzzlU?U}0d9niN^sywa{{15Lz8tt*;NQ3vJY`Lz_a+>YGD94E<1-hJF;sg?8zV zq1~aqx;(T$^xwKm=vSd%>8_FMBW-ncq+O()?ineGlZkvt|Ez}S%lfjqQ@@E5k>QSmBhJTg&+!a38sC33Qy+I)IxW>bPJ8E8 zHO{%s8G%)Alrv6!-5HOw_fZ_qrUIVbskmE;SSo5scpDx_E*$@IUAjgYKNS6tDicX@qO7&=Q-!+>UrlEIP?CE z^P+Q9y%_8k?517{elqw;bu9SV;AhqEfY{H9bq#gZZA0Bc-SthO9-$t( zB-A@puiJ#Uok`ZZzk@%s=hxC-l(8y5znaG`y zJN4Aah{yy)u%$Yfz8G7s$;Ng zur4?#I5_yR;OOAJ!A}Jz2OkJN9Go4T7n~nl5ZoO6LFj!UoO7$pXrJf@ql2O!iGD14 zSM=`a$D{W|?*o5baC`9Q!CwYn4E{ERzl9HFhOP}|hjK%Cp<6!V^6SW<$ZsNt zQJRh%7nvb7)R{P6S32}Z%+5&o-tfIjg(rn4;j52n;c58r`B3;F6$sA{&sV|l)8YS8 zp~#WQ5mf}6=F#^>`$q>vKNKAroe`ZGofVxOofG|1^wEs_Gj_q7lX*>Mj#8Pi%$SnT z`Z`i2Y~)k~=iZTXkvCKXtdsHnjE_j!84qN9Ua5?SGM)f_GGn!Bo3SQiy(-Drkg-K| z%J^}{PgT#1*E2KKt&rnyJla+H_w;;KlZ*mKB_8x|CD>@o--v=k_=%&m;{oLM(9W|L7Lcr z0HPuyA|f_GrC1OZ5gTH6l~q($S$nTo*2TKYTF|v%M=U5RqN~_Yf6sYl^cPm$@9y`< zxBT&AK0LW|=FXjaXYP65=Y8JyoFldPO!{EG-^2d=C}%0DX34M6$B#q*KVGdtpMO&$ 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쩁>KCRMjZc^qGf4CMlar~J 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П;IU)!}uL{K*-hJ{?a*DUv+j_NnLYu z!_)m+w)8ixVm&8q*s_7m_+@>Qp9BL;io&S?_X-PAdO+@iMB7Fu_N~F`Mf+H6{KP4x zCp*_%TbJG4rfO?_@Zqb@+UpN!{`65&ef3OU&)((>avByk^yP*A_UVce|3a@F%1O}& ztrfxE25C2Jjnpc5XQ&Z^EM~V_r~xsJV4%qP1b7J)*vukihXcl;xL2qtQcv_vaSkJI z5xjyadN?pZf|aF9woo|gNNg*Dk~n)J^EDgq?e9?L(gfb@um5Lwv%f}pv#vmU71*ADFK+^G zmR7aW)4N3B&2SGGcgC>RY2ahhkvrtSz{jMIEZwzlVQ<%p>IS0^e9XFqyHLw&}n8rPPIkv&y`Ac1xyXWM0p@w3}{9pFPmvw3!OP$BdA87<^1J_?UT+&wB+Q zBSrWa1P0g4ES${8#J>Mx_!v6apO24;S35CogqV>cSk4nO(mJCkP*hi0j=8(0U`G51 z%9p^5Ou>tI5}6UfspY2VcUbzs_1#J-2_++%`)^=31i}yhsUu`?QLsGu-t# zx1PGg<;}1`u~SgO4`1`bGq(w5qUa2o=$_H~O2)1(7)Tkr$ zn)cUZdOaOo23d9}Wces`xhVg#op)B*7gRbHL(C+WY4?(I}K1@}}GY%<+DF=7^+nwBBbonn#*j&LekHg@pf%`N!%@EKQL zu_wn=)7a>Lz5k3eh?LFWz7eD>`0ECQ@l*C1#JiyR6qDiJ!#H+oVu`E(F+wrGJ`{{D zY&oQ)L5VB?5T1~;L2cASK8%+zPu_1XHY%w$sFC(v7Y3VM5u-#9l&^!AY}H*Kk_)BN&h z$@SIcyn#K>*ATs3kQe%Z$wY8H4q4Hu(VFJt%-K(X-YTc;MCq;Jc<3#$!dPXd2f+IKtl}~*GPJa?AzdcSNAV~n?IhFSUE|)y{e3a5zdVRQZo+yh(`3Z? zjQq-xSajPPpZyJe$l^eOda?#G<7EiBh zQE5eec2&jZ!IAlpDa@KXtGFt=#uw}>J9A6cZ8vIso&lS)%$*Bz>|W75cO?C~Ycggn z4v<(OKg;Cng(M_SkG~wD$4&oTdQ1^{KOa3-&h~#@uJ9ixseMyT&w_MpwM!%DZ|}+ zI$BQlZj)*G=ec|T8$|w_Ao8vF{Oi%+oIWD*7lO#+UzE<@CFuPB<@ejq!4t%;x7*i? z>up1$xZYyz6rSKN@B~L8*RV@Tpfw;oYbMu;y-0kY zH_ACo!89mcL|#8}9)qkXkHTe4b*sOoE_*sIW2##{lR)VZgXZBA;xRtoo&JP)j2!S7 zrCFf{!DDP44Gx3HD9U#@A9^UpW8hZ5e7CsOSC!&g4_VM(F_&gV7IXwxjhJl6BwZKd zIcOUko99rDyG>y{#_=5BG>Ahvao%Jwc>K)C9V_YVyA_Vqvxm~2TaS@k7!d{r1z!4Do4iYYzAroem z)%iwqT28)Uk)@}>QDrfj)6G`>LQ8kU@3ON}Q*!f58afR4BPXZ00VjG29j9H|ClB)u z%;1L*-opk%ey}KNgHZ)Z(cAn6iEOJcg-4(G&HjRSe~JOpY#+>uD2v%&s1o)>G!6mR zB@6u7D6L{kSOX*yiuDrq33$(8C{e~4;)8`0)=R>c0yY?p%o2KEd>R(jECPfYrQ4A< z_8(|xT6?!ZwBA32420^;Wdp1i_HG`?8P!-j5)?v8C`O=GlMYnP$#M$4Qz-ES4C?#W z^5yG>hSvRK>5?T&NlHHvb{NT`@F#K=e+~YicBxcKe!R$G$mqNCvv!a-H+?$9!J(H>E!G5md;kK&`%iQ%4N!aa1R*w1igeAwOJLAT<} zz*8oEX6mK&>`mBVQ|_0fm|YT-Mo`KJ*qf)EGH~+l0sP#3GW%-uFFY%AkQ~6vJ0yeD z6s*t8;OaSW%x0a?z)@NS$WDf*P`06_K{AjN1U^ZWT|jt1z}Q51KiHwA@)_Bp+Q&k@ z3u>DW_`eC8(mw38QwR3!FCW-5uxD@CA_9k)+iuKlegq!~dq&`cxIJSdJB)I~PY`pT zZ132}RfhkPgiiv+=ak)b)`_xCpJ z=RalsoRw=l`!wfs?yNG4tGv9J-?ROpeZA*&HEy0Wa~;AhtXyZ^GIryVW!3_J zOP~!z2L3<_Ol6!z3rVr0Yei%cjmYjk2af3xOP^v)8WtGRnq;O+@rwXZbBA%E& ze2d~J6erik`zn=gAXZIBz^v)$m@y*?&<+?&b$88}J)?bQu)Mjv$?uy4+^iD?u5H39 zsDxW5R>2ni21Cx;rsECdXeguz-`KKr& z3s4bgo$`kW7lrKZWg!kvN0{pHN}t9P9u0kai>mdeH;cYMEE0a zuIbbVt~$d}TY%{dPqP)~i8*Db955K{{IjQi^yocTZhi&{-u^kJWv`couI2BcuP9zl zg{NUAWq*NjB05A`V;J)(wjV|uR+uj!%^L+r7ZZ-#dKkb#MSNG{MX(47~|M9XKR?>|KDySGQ~ySFR%UCe9B`!4F-&HkX= zvy9@NWt{$=6~H~|pFD{EzGtNVpnk)%xCa@PdysMJdk`Q5$|sNfzj*KYLFBIEDj8~; z6T9~eHHGe*c<-^*bjR;K&4FXwdjLN@_y{ZRJ>Z}E6?jJB`_=^bzIAl?inZU#{((CN z_$N_!Htq?3z&!!{lX#v7e*Jy$>)@Z1a_~=o01poS344PJIlr2}BVuTM(L4%*WtAM0 zM}b&Duqa7jQMUJXoYd+Y^jFNvGoEv*<9W9;;Hs!7;!mCW#03c)%DSE4P!23v)+87d zwNwiJq=&zc-F7_u2`oaVUlTrXyM?JR}YyHtoWxXgX32bZ7zr2e&i8W5-R@Q_t=kl z%eR|fK6U5se*gbtx{W;-rQ1A7EBKTDYiJh6W`k}zRk`!06aB0P{_j`r{Mq#E22Oif zx%2Djc{X_c&vkxKS2;gqcdW!Ai1Mw`JAVy$p(Dzj9}Y51+J-Y_;?Kd?Nb^XVM)BR% zYQ2nUp$vh45!;h)E7-V{EnZkW&<~mQKAtS#6S^;Sp8}totws5( zmxo?swaZuM7g^b#lsR3G@q8E^kMNOZGz_!WEu$rbfo1%|&_M-2p*jH|#NJ||g91YA z4Fy7M2f=4P;Ny=xV5)>%*U6Ouca!F&c>0YjB&jw0LmFu?QHkR}~nsox^Oi0wnf^08;3nA{(bM zsUF6dawo1?WK3ugLln!WdN{g#b7JG$AV&Py_{EHkc8~_ItSpUtTi79+=C>nyCqhaDyheMG#l*9zg|QD! zmNMRnCZ1sDne!hE{V4RK2Q4PeNzXh(x*jpkJNPy5?He~vxCYU{e57VTd=fJ`JhJcQ zAEN4qET+gf`A7NYP$pT2u}{qTV-G#`6n9QB&dhWTG(fOhcjBJRjCRECGP?a*GgJZc z4f*D=52*#7jTSCL3!Oi;JV@YpFyDO5O*G8gX`D>z!`z=0bJskgRb%gtn|EL0gcD~f z>c-b1n#UcWiY){)n_??44)l?_QTS*cQBw~1aj;1Q%6M?lnB(h(epRSD4hG{VpD=Co zix{u?;Q_u?!}zi%m;^^o1eK0C!%%P;VRdjKxw*6SO5R+jjG!~-fR}r|{s8_xvth#q z)*pI+_2cK)KY0H=(f4eR}qBXcxOI^yXXqq2(bh z`zPo}bi6#NRn*VI_^seuX#Dl##!oEExbX*JUWDFAvJgqaIQZzKv05Lz|Lb4B|CtS& zHnE}5SJ)6+7W(Q(fBaC;Y!D<78w-6y7!LIlVv%VOi%ii)PiR|-y`qL|D$`tj^+x$> z3>WZ@*HQG~s0GoX|tQWbE_YoOV6Qk(RM zVm6V@nNEvbaI&OB(sMPXPE^g!51N&)d6KRi_Mg!b^<4NS5{OEZ86Yodb#T9ALYzF@ zodUV1q$C($My^mIO4Le<5>*C25(Zy5-1tx{3AGTDOHwC6GY+*A!i152G5Y#oFo#Tw z18sp|b0bFTcX>Q+x67RhtrJw>LUb9?j4GFeI$_Na7raM&0zHa$m3&aL0)A4QQ&rwD zRAkKamfdi}#_N0Uzq4jjvAg=L8CU$|lr!GB_@Y-g)val(pPgiwQ#-W$jBS}_v$43W zz{dV=HvN4@)$p$KuO7T@;~h^JGPWAbJAZKL`EQ@L_0f&X4)!&dl>5(Ecji^0-PIo@ z^)7F3!14gce$3wktP7#tua!0j&9y#vPHJ+JRs+FTMjCnk%}r;~i+~x1%TTdWOgCJC z!Tt|n;S-Vw3+)72MgEaUsc_LX*ldziQ)Mf)m7pVQ77P)xBMN4%nE(@U+ErX%Bf1GC zj)|}#aB>BP0WUja*f*=~f&#;SPu=Mk%wKVK*Y=jSj=GYn;n@~PuFZ*X-!lrDt6OXC z;4hWTsc0G~J*Rn*cU#Zq*6!}ArH#$YDr&nb%DXGpIScYiZ04%M0(Q^W)?z$O=I zwj)wbF)=g0{o>^ppSx!=cS84H!I0uI5@x|oTT}(d%{4#{8m`s|y$F|9#Z@Rnw53ZG#R*~$iA_DZhVbpf zB67;jZME4bzltqjtE;XoEAhHB3p1^Vb4__xQXtl!crm7oF~U$sm>E04Z73d_$g);L zVHrUoM4+NS-?VJmO)FM>X=vz6D>`-rgFBGe96x7!XXRIC*^Kt$t`bjK{k;Cw6@5M) z8oY7&@*4&RZyZ{F<6v+5$sHZLXU*E((Q$HnLw?ST^b~)?)`Fma{mSM+h_zMFF#U)x zfL|xUt4#7quN`(kMNINx^69K38wgnU%iDp1f&Y_mq-l3X{L7Iafem#-daoOj5bIC) z7?XUzJXVb|C~)wHa5A1yKcP~vcKY{$e$*#Kp%#K|r_yR-A`dGpSmGw1Ai4TCi` zgAEOXKHp%&j>7!8{f(y1MF+03mX=%i-%;LkR%ho~J$N@{^RH}dT2&WV)!4Yw|4C8u z$?Mj!yrmA84O_z>z8|@#9+GOLR%vlC%K_xuSmVq|g54U~Ge!+kvTz#k|0GjqkbSC~YAs&ap2csyqW83bXYz=$+Bo))i9n0op zOQ|-eUpuq$OMF@A4OTN0+I5GwXlCedEUA0;9qh8tpm64rm+fH4dy2gs%i&Jx=Yf4? z#4!i9A?&N9Nu+Vf5?_O2ln&q*w1DD806L4dS!RA_o+(?LWhK;#ayV-u+BXHyD%9ux z{#xI{^74M4ufM#|l)79cB`y~^a&f66{HeT|e-}1l zg@8vvjy?(SQ2Y-9NM)FyZ8jP05=fjDT8?+D*Yt$o7m)H6#XLhc*d;R)*hF9OWg)Q(`eftIUMX?}X!FkVx)B%0vrOU(@N3ab! zBNM+ZLfdKuA%%kpP0h&y$%Vy%+4I^ht2YhK>0P}%EkkWaTF@=(?ODBD3yW&nSn|4& zIc+^x&3yvwO^e<6WYr2^5c5-k3z1awu?}g_Xcqy?JQhsOM9_mArh|Q>f@qvD43l&k z3HU{;MUYNaGS&gyrB25OK}e$^M2V1iqpwo%^Ff3-Km`O#5EOOX>%mgI0b`fNC?pLz z)2@kDMJN%Du!+&vbs{>dmEu>Hm6jAMF)S_lbhphwAX+IEY)KTG#IUkc#?lk&Fo2Qw znIc>Y1#pvbos}OOtvz|&`jh>mvqJBG|CQvFt6qEEboF0Kii?Zz{}0J@h`-dhzHR+k zXKo0tZyIalxxJ?^4DrKbzq#mima|~Sto9i*+GouOnH2k?ES|wFat|hu8#Ez;3{o{? zHTh29S7{^MoyfEaM~4m!x7@U4{Vh*j!82Zdnau#M$OXdqJNWn%36W*V*wze`J=hHh zgV}{DRzeKSLmuRtvTw;tlSWv>L8mjOz3b z{LF{PwNEc<#j4|yKt@ku==4K4D2?|{^84@{nFURS-EhI)E3e#p!QM+R-3y(cbawbE zRSiOwWI(f1fM`;s!1N8k^|PfOsb5+OjJ{6VD(#leki0?H8K>{uzIDsU@Tz4?7WMab zcg~(Uqp7~Oro0%jn-Sr~n9TKbNgIFaB_{$In*AoP5tf7Cc=1pdPYTHN!;pY7fIvwi!-Pj>I>ZASUGjQv(NZW_&NueKW2_bzJB>>o8)&p&(7 zQgh*&HHGGdgBP0SEjUk>&s#Dl^!U2MoTZB{koWiX=H$#>ykB$n;KH232f-j3x2;;U z$teG3<*dx!_2&HD?N#~a(SCuSqIYu*j=CANA`LNeb4;13NihG!CErQX5_?2cVj!^z z7&E^eV0M0>h)qJD{a8p%Dj`2HS(xU>_uRuxLVEIh*8bE}q2GkYOcsmj{YLs5+(Avu!|J4kZAB2; zc$tNnSt{83Bhufc52fEqKbL-t__)tWhouLluSmB`*GgAN2Vfm?vb0@VE3HPPt3I66 zAS|t_B)4RfjF2g+F+YP;RXTA|vOo@Ty(Gs#LiEm>FZfyY1-A$1!q`~Bo6y)v^yT`3 zyNL|j9s!H}8thlqYT8Zzx;;NNRX0~hKkhq$zs{!`=O4NG1ga>VyppP`qq-~Uhc5NV zg(uv?*rTYz*HQhk=xeuOl^NU zAalw5bH1YQ3*9!8Z5%>M!w*JwKDO!J+gDxLSG~Ay{r2=`|CJYwxHhdyZu2`mVt%!_ z6aI_83@dr&{nTP-eE>{19-h`324I5LYnp%N_# z3Ed%epeu~lV^%JN-~{lcnZ25~XXD9zKF3VYB7JS+{GKvbLGGz*w=b!i(LG;ZQ@3DV z?`C6KdU1=*XmV8DSL^K_tQ$5OtBq!Jrn$YDZi4~$+@bm*PmM9xgs*0FS6AyY9CQ^- z*5%vt3UQ~xx;MdI?sonT!@kJQCf_3dV7B5k!X!J0WjgMODbhM6Bf>HG)zs9ITz9asz4cVjK+xft-*3VmyT z=J@A@SUkir4Sy;<%U;F$n58~N%7-PueF9Qzx=AZy43Rb?Kk<7=fyE7*L&|~h*tlSe z^d;t0#5Y7^$u4ohCIPQ<5Xa>SxU#a+;Yj$fSF7yyD*A-J6a5QgD`X!af6hO^(dA3? zDaI<0L*%ZRf$I~3o+y!4Mf6Cs5>^!f29+;G_*F*cmQzMqrIA52LGclzcjf5Q6{V@^ z4P9<~x$UR1zxY2&3iY0XQ!dMO7ZxY{DfUy9^cQv?8-Yk|TAww=*94ps8-d0fd$3Ac z&mLzV0aVB7vo_9Nw(`OYPsRUicnsb1ObB+e3XwBje&i1EXefOc4-G6^A6#TA{>k?Ec9zr_3}@r5H} zppE0&4Mt?;BnwBIfci4LK?=5@Nx&Sij<$>vsM8ftt0lEwuZYSiBs*Ie`tC~*C3LUh z{R!=BrS(eC7Gyz6w7$jtIvQP)1T5K@0tpy!V-{~k29SqG$Rc5o950PVKvnFLwUQPW zXvHp>WO+V6TX@n-Vj%3fk*mhdRZC%LKWA^jR5UfNGZEk>*4ecq?CC_fezASg!~o&w z#7ak45dcypJ&fGRw_sRyB$mMG*a;yDEtQa^C~F1a2*sTnYeX^yc5>)j+eddiJaEl5 zf)=JgHun;DNnG{5c&0ob8Q5<;=`{hns_-6`Azc;06|s#lNOPSBp<9xv)C~F0Gnb#b z;e3{ZDp)EPbFg-PBkr2H;P*^fDWnHC#BgiCOY&7tG6g!F{$IG@lv(82w4|wV@wr-H zx%4!4*yps^Z3Uj{_a1p9^rP(5wCp5`JYZMd=K7n0&X~?ijNz4x0$t}CwDroKI>%vz5JUE z4IBIVdRB<{TltLe4`eNFXud}w7bL~naYX|*h)gH46C?0deDlt_J3i$%F7MhW+PsLL zhB4FvpU!v;I&RqQMM5OUbMc5!+)D6js53!5M084!F7jZGrs9zl*GOPEjWhK7WtsuQ z%r@hIW~hgsHr$gje||;^Wa0-+?wnN;VL3SC0CfGsi_Y-F!h!_Z=IM6NIlv zDv&0m1i_ph3ac>ZLs;iFCFdmx89`6hW0Ug4Xww4IfxPURSO5#j$oWy*jd1=jIztb! z>Xj$29Qev2asZ+})R8{Als@cJ7e7|B@;0j1Zg;`cK@d* zuME98uyEmBccCk+;rcK`UoT({FTffaB5P=ft>JpU25VRjd2LoOgPbm~sZevHp5kB2 zS|@FC>t(ACHXJy>KfLIowjt6*U=A z5yt)C@r+wOY2Us!0eaN?ap<$~&q4AbH3^o3A&?<-r;NbRZecEP{(uTlY*r#_%Kp(C zTu@%+lqy-o!RiU^70FcszDxbS1?`T;Y?s4YSyq)XP^Iye_=5VhEcA(+l>dWfo&P8n)seoO$J@hTC+mj0HNc+a-CY~Y%mnyIm z0qHg4n7!`u)QpPWe0#a0z07WKT37GN^V-U>he8899R9U@3(k;PDhn1*WGI0zF#X;s z_*^9fvQYKY<#)IxCF98u0(J*ox%j zBgykjPM08ivT}JQToKhUIbZeIjRxEX%Y#jArNt}d#awU5$V{7=S6|_&)Rib}Hj7ta zj%wJ4Wd_qA)>lqazF2AH(i#wedn!Lit6gvCo6Yy|{Cs0UR)(v#sh!&3p<$evAIk4w zKx9j#RfC(V!a0PECq6&hv2ZENJH3BczUNW6p^_bWn1a@w|S=<7B9Gt~RF*bBXpC|{%sd zdv>}$DH9LIStb0R>1kO;q-dO(k(Q)0n!WxY?W11+U%dOp;fwQ+i7!Hb$GLwliZ2Ks zcLE>^K2{U9@M=TrFB`tB{=oUK03R=3j#7bVwqPymrmn}v*W>p`D#CBOi+6^9E6)La zxB($2E>-A*d})+E{D<%m3wQFi@CWit;5S#$4(k<2%`lDeDK{{&Xxa--*P%c(W5}g3 z^v2uD99aUtdF;S%EYuvf0)F4YzUhGv0Cp)tXW-}DoD?pDTgt+{0j_)adL5-?L)dR1 zd*llfz7Aa8Iqu8B#~p|@=1@c2rX@v}7av0YVl(-aUsiRKZ>H5c%h#l8Q|Fq@x#|U) zT$3qR-O@EQZ{ARs)`5RcyTgI=ww>?58ZDptY`?f*&nBi@xMK^y-t(CMMf;;Qf_@K# z3&Ot+e+%n#3+VUFO1zRIH%Hco@cBOA^Fnck;k8OU<~X^5IK7kP1`#wrwM63tFnwVn zmZ!gIF9O$}ig}h|Om)hLPMjZ%%T!_{pfpYbsE@E7>-=r>;aBvs@jeh0Bp+s6w3`wc zjqxm}CN30lexnNL-*1V!``|j^fdY;-jL)O zpp%KQJBUw1^bWijzwv2_{{zmwd~`dT{lGQX42bcJze@<)dvTM%!7lQjeAd;DooDhN z_w@B`Y-k9Iey6^_fZpBRx8zieYwG(;*@rFsnFXsN^st-{grAdr(fbI68lX^RB>zw( z19S$aFfNVWN5BXAT0gyW!RfE^fqg?lz5^P&0yK6JXl!pVeLRg-?#$#GOgHp3L}`g} zj7f!+ZzmAo!N*R31oLO3Bsfsvt<)hTWax^p1GpMOA!$x9nHU+6ePOwgikW7iKycU+ zC$W&oP^%dGfwzu-r^qQ_OkohRX;tL7E6IqRq3<;K%?L11aO|NOqB|Pc@f$l+kohr_2wxzy97o+8({?Ice0!1cYgZXsr)DVzz5NNtPOKI-f@6` zij3nSemT~B4C4n5BFsB|5~*mG2yqXe$d2*LF$^)U8JHKz3}{~CZnqSaciIF(%DgC~ zpLx+;-~9AWK4bZ|eZXNahGoEa2-bV=gf0o!D6~CX6Q%8;=OGul7d9Xn(&|Sk>o5RG zCq>`{x=s@!RVD@j;`q&s?~M~*1^Srq7BZ_m>U5GNy;F#70_5@{DEX38{5kFvhpVlB z^JsZPg>FO_^v_(9k8MHY4&9ISco^gU*Hq(9*n>(ANlG7$u7PVvzeXlANFyD+gi~Z7 zvVOY$^wU?Zx@*ZsNWrL&*FrC$kJms~9|K+87@?~hV|4X3;I?q31?qYhROjaQqY>=qX7DcWhF^gOw>ns1h5}R%1O$Yf2~yb!Hwpa0y;~xqeI=4t zo|Nkb%ca3(TN5=h);fhDY*cWAsAMaf)HA_qnQ}D?)VXG`T5YN(pS?OGz1m(c7%sb$ zm@aMCP!VK=z@>`3<#~WbU@eS|ZW1ImhOTune#wLuWi}6K(X}cqvZ~M&A-%D~B1v4B zLT`R>W6M*mt?<_fhsSi%k?>~>xfgY(9QiX%75nL0eD@>q-RU8L-tgU~a5?7xD)tz} zyhk2Z&fk%TW9M%ZKLdL#2fXS)Fg5XxqZ~RYEtHt|YvQ@xg|&As)|}P`gn~3!32UTC zg^uGI^w_l5pfjep24OCe^tOA*J(bmA;!;F*q_DT1AbLB z379C%1ccs#uvOG%G5$49o!FNVT!v*Gw>7C3v)LJ-p3HN(_prI)p9>H{{nK z3pmbu)%Z+|@!X4i?u2}TB;)@+Wc&^x^FN0Zw-*a3a|86-*1hP)@p= zAa^(?Z$kmBdHCQvjjnbn)?c{AbJ`ea_C=Wy)a53i<(jl36uhwb! z&X|e%1in*2mGRgthD2m}D1t2c19>?$3kVg9xkcx@CqCcMVM3xkl#W5 zL+pWxZOdt_S3Uz}UuRnr+ZOeH#%kqXqwF!(Kd~PFC%aC*4rNcXM-%JOm?Cib9I|s^ zyAu(gkpBwF&cAgGSs3=u7QlQOnU+D3X;~FfSWKivIM>Sev9EryGLftViFcy(J8I>@ zl989zURi0ErxJ;X^*j}8{cp%b5=U@XR4hJDCW3iSiC+=~0R)>J*fSGkP_%zZzWnZ# z_eyDm&t?IcVunWar{Ww34tB2QzrCaQTLQO@@DEtM{NjXn>M!pcme0MV`0)t-yPWm@ z8}}sxu7Orotz4G~rl8T9|IYozKH>htG~xo|bkYT;tTFKdLoi-}^Yx#(J0xy{M7jF! zCd$>PzB^!SXW)GQCwWd_XwQ_Q_s|LPoLJuxxQztlB@^}(?K?SJ>^p(WE(MJl2EHPC zY$g249<)Yrs3@CIU(nk>2*tjre;sIPKkA>wuANk0l!3Q|{xYI)2R$4wR>aO0dUXw@ zwHBH|p;sSAPp^xs)dg<}{B)z-jXw9Y$0zkE$`a-H5q&H=m`JmdBMMoX$5b@ybK+}F zPU(r`YXQrPF|S3K*L73OOOz$fE27uHye8oC@5c2Sk}dQt>FMLugQ6+n<`y%0yE3LI z7^epnW4#S@c?QNxbXn{huIw8+zqf&Y%)q*yh>nytz;RQF8IuI(VocY9Ms#6J#1Bl| z%c2ZpI;#G3!XRRVqAMmm7yU%f4Dsv}`{g9=h%>vK{$kD*VJq*Da{Y#`5 zkkJZ(SHfvVIqQY6N@FTLZe%aUeg<&;K8&4ssfn-e#R`IBK ze%}-_A~CKTf#>TmF5=@R!c>%fF5RleOla;z5&Vr)2o+f9{~7rQd8~5t< z!DR9lN48{PcuE?@EU}MZc*^3s1to$ksiDa5G@)QxV^l@-4vnWnL=%993Kl(4Q`FaI z@9?IZGKwqPUh8ad6c%fBvtXdlBp-W0`ab_Q#x>)xVkV+of#d41;YlV~it&h|4~r^T z1?r07&AWg%E9d9g${qaM<@PMQebvr|DQGVb7ainUPW^`g2=$t}1k9p+AoKeT!YjpTqq0Qe!X$5#rJ5Ts@11YY-v>I^e@` zk%7>%MMVZC7s;RD`rh8!)Ii7ZLvB zFi8Kz_FfDISO3>wkiBGC#W)x^-!qW3k$mJAvfKEJz*!zdFPR`Mr40yUR=A`tCfDbb*vrct z23G;HLXVr{>A`G8A`D5P8}38$Tm<)EuotAcLg*izLTs+^F;cRci^)2UHd%5qT)bO) zAIXMdcT{TU{lWDfx#|PG5Yit{JK!?-AbVMU0_{*V22gEac{=n^70dzZ!Ol`#FM6e( z%uJO^iMF81P^A}Xl88a~281PE#33Y6RKP~&3>5Ui1BARnSW)B;`HrX4Uw(cPH%NO} z%IEFU6Y^}}KDvJhWIMeLAbKBJLK8rc2C}}RW|fqhW(w- zTiMB?UA`lO| za<0IrglNckZxU(+tP;mQFG$%oFm~WpkHD=u!mY*}rMlP%S+B9P+bwpu^wCY3wI!jN9&ShC=0-x;TmipvWZPlP9NB9y8|Fjb^Q7v{p9 z8tTaD`M0HoplOBdb?J2;g3ZV!U>CP5G>vbM(KIV3otZ8Ez@__-TteGHn)%EwclvVS?k|(Dw2BIgHm~O=$aX6Wb;kWbpXLO>%>nfhze> zOlbSM#I||(9^_g(2%WA0TI!)-sv#8~l_t(%$q0u^THRbs4q>QdGBFtJrZ`3tnHvjx zSu8P}$D%JJmJJ5eva=}?dvTV_NwyqnxNSukq8fzr)n_B53k7k(y5oM!c2t%Z-PhGO z*q?2>i#7Q|Z(4b_$>XfvW=y*(7??4O^IOL@nUeK+tGtywCkOUBuoE~EUd4kLdy3Q= zOp59Km0_m=A0j~s*}EwjN~SOe7h5TJI-veO_kd&@ld$gb@VJoUiCtJXg7@Rt!c z^KSM6yPAUghR8|~bfhg@!RMpjOvx$T8BEW_mBWqzni?i)C_Y*-pyosR^aVCudTML;LQ=~}!Y zOMMVi7@b*eqZ8LL3b|>_Btz`Texk5rB3OtJmC$j8YSHVHEv4^X;L2RmvU_KV>jt~s zQ!(=nyR)>&?Yfn(IlMRY+?g#cH{QMX(S;-T?%cU@*&AubZ9lN)g|f6Xb! z-g#n9WI}mB?6ZjHcn%^u$g*k>*T%taaU+1Ffh{BHgGEwNA!ag6QVQ5EM9ax|A()N^ zDJ2g5(^3crJvnT_m<5tcW*-e*Jvz#^@)@BSd-kvwC?gK`8_sA6&S*W(s6|3X#Kbe2 zaP}yA*=iipT;7AD2Ln}c^dQHd{^*I5SLZGH+MM2H3!*2lKsIG})o#m4zPQ!j*v7ei z?6-9A23#e)%ti3Pxw`_s_Vt(u>}`?}MO8%F1Cn7IU%l->~Urk;5)9nuX+Ink#=xaKHf!OB~##tm)P~=V8=!rW#H(`fcr5>Zx;5NEk zT8-GfB38iU{fjs~koCnjF#jL>)}h(+(=)p}mT$5b9?Z*gx~57u>)w3>MA;w7icBNRS;-DwKWpm?OHcuqajBQBG zgbkUzDD=%mi&zJHIrO!iJK5qLJJ2r2`Ec+zaOT}owe-zkzL}}y9GqW0)9@lM*J?C~ zY7cn@(?o(8l;E6*Sh|S6snG~@INGga6blb0WQ0UC0x=RzQLuLr_*&o#ggiag`$1Ri zRW50?2(*HRCFG=s!Fjw{1boZ%cs$jfDzDpx%ZHs3Mydq@@kh`|9iof~`5?UQ1y$0E zr3knXsaHJXk-yQfaQUXXfi>&>!@&hr!$Uny6$YyzFSj5++hPtq{C>*AEdXE!v;=HiyH2VX_8bp61#svZ75Ga|B zsY7dorwzBS_pNFx?ab9(s*}xm$(Gv$r=GSd0slfc0jF{^mA# za%oHRFIP14v6owV#jeiF*@YucVD;AOLr1<@=E za2~y|A5NAt(&3Gvir|HE%#EyA5l9oR>G-TcR2t0?&iqh!5?#p?AUDViejnA<2E>*dcDz} zWlT+ma8}usFeLX!E-SDMhDQVG6o4~`ku}05(}=X1{YuVq?X7Z7fqqdzR{)MYBYiW= zib@^Uv$M7&BeuQBVE)a&=jE2vcd+_xm3?a+>$C0d;%&P?MKLV!b@E%>1^QBj_>3g0x+DufW$B&qe8$w?Zu23s%!P&eELC|~Xx1d*3m5=AIYPSy;;Ymx#} zl58LWki`>U2Hck;L}_xe7I`_f+R=E0V40+WnGGUTYEiD%CQ~TtaUG#98>9|2;Bw$n zR);MEOzYGekt#^9C-@;E6+j0rRD2@m9qtM^T`Nfus zThGRS26OuC%$CZFQms8z>qj%#b$6ye^jz8- zKW8RWL2b)htwluueevS%OX-XJj!vhe$)Q`*>2NL;=O3`<-vO)(K+zru=43IQ+Qn2l zx3$0mTkuqb8Nf-8!q|a{6MKif3K(m#XLULy#60}d!TZE_s%O%hVl(S>VvCQ)-VS0V zurn89&2=gb;@3~?K&=*Rm0Ex{)oTz?)n_IVP@^!N0M8gC1@h!~4cvM#4n4>G>e;-O(OffXz+iU2BUX;I8Wco*bqxu7@^ zggWm1U>E1Yh;!?}nBG$2 zKja!gVqpKEu>nzPqlhw(sX+5{@k>2Lxk!GH33B9!A~IrrlwC*z*40eyNVzbW3) zXw*Z1+-MZpVpFOh7F&#i4(bEgMlfZ(1C@$ACRAcmLT4tRi=AoLgS_XLx~9OE3UC!% zNuXuxEQbF4?uMjWS*|5(Vcy)p#c5ex6$QC@)~t$}^kj9&oLgK!`@$D<^GX68{3q)J zt=b#4ZEeUTjDHFY|H?hMDV!NJ=73v57G*VoPZI^7vcc@p!HerMm=*Xt>!8X*b_RWP%4`Pu#XJ@p_6G*Qdk zz^3I3$&1jCZ%tzbraa8@tM6l$pap3wMii&QrC`x8N5t9r3C^BRS`jq6;DqC=sR9p` zV+0~*2wVX#0w8~qBN)l=i9`)zQi7ot4BOG7vKM(AHkXHjF-h7OKNi5Ir#mzvVF|1v zVuuMNtx!1;hL29q|Ht0@$9FmXkN=Nz&h=|+t*!lRZMAB(rBzF-R!uFfgr#9J%d{r&#)uG@X**W*0S zxz2U2^XqwC=WxJO+L-da$1lv9Jbu57{xO60+#{)5#H7wWdkmX3Ze&@%QRDXR*{jQ- zoZj(0VkW7CZhc1%pR&gueW&K8B*!JjMR#!%W7D#GWDXm&?||Mt59^;0*Cj4KIx-@% zM~~3`ZTpUE-^^J@{pHev?!D5x8(-7J1ni*7As|evBOJSiZ-->|`uWO76yCOB4xQ-1 z@&j}9_GchcMMV5jB-wZp%5ubvzeYlkqn0@rxm`ZO-f>>-AP-pyarUVBgx(o;+hw~A zxz~r+GyXId7!e);gtiXz`t-5WIdyv4u(Zkj?)E}sf&KIL-~akWv2z_iDXr_lY5R?I zl2gjN&W=x1PZYKf2J+`E?0U|;Dix~xZr}7!S@0N&`hqoyUb46#If3RNwR>WWWl1Fk=Pb5YQV z9Vfd&Oc0qmU_>vUgKKToZvP;Q>O=-Yqd zH|jFn2`RA?;|oXh&R($K*kw*qO0V28i6tk@y+S7?^~%kUCqsL*UE-`}>^M)2Q-`Pp z>Qw1IE=rxED%EjnhMKJQR-m-0-J2E2<^6UfcosAH_8RYyXlO=6PN;ooBG^8u{p{Y$_REzK;WgEyK4jD$=SKMq z4d1L0nW3pHCrQ}yN@B20YUt@VjN-^lwAr!|_)9Tz;R)oUJ!)UdkjqI*s-80Xi1~Tx zhY$1yXY?DwdG$IWt9#mrQF&B2-f;4hQUkAZtQS8zCZ=yp+^FO(X-RHIpDvLmno6ir z`{XVu$Q+yDcvxp%~{Y?HnCa;4%@R9Y|JiR83A z9#l6{>)1}ZYetlcW*?9tQBIII*z=~Eg1l>oMRW_C*uK6F-1uzQ=`t$HXX~Q=QC{EF zOgB9xGP!6_?4pR|nXyql+^D#O;X@B%L#M8BMkTR8ogVGFaZYrUH_Yt0f8DT*DN))F zyg++Bea=|tpp9-N1x~geUzDwNbV9%QsOY!|HCd0Eo!+gp8=Kl|;#kkbtE9bqNAh%l4#ta%yhiigw83(nUa&-Qw4)McUMlgK3$aChgNKGeCj~P%d?}{RVcCRuq>TE zX3&^{J-S3^433Rp0X{Azs&iaIT6{!bKg#Rc+x15ebBu}U?HjL4RA(L6cVJFzwjY`3 zIcfcpM)dbmwI7iX*(ovT+P`zIpFcD|DA-Gn3NrTY9OcBt`Xm3*H#pq52@^Qx%n6d> zOmsIDqoTc(pH8H@a(Drg5T^Vp&M=BfAdn(`Vp_bgu`Gyq9kh z#?0>=5v5E@mzcCT6Q9A-XW&J7k%_SpX~WepHzTDxd%Y#3Bqjyj6HR9~sdtc;kf=Cg zH0Y8$Hr^cSC_jaR_oAYsQo1<_jx#0EF|nMlq1n;26YU^Fry)jj&Y6jGW4o$&R*-2S zerXX~?zz z&9sktQUa<-m{ud6Cpa_IE`@}wEx|r^nSSONZQG8}FDN{s!*Y4x?Q3%*MkI|GK4K_2 z4fWErclnuYWw_>r_cp**!b`fEWIw!<>Ja9u@>hTfXoP7tT#t-&Q_dc%b(x;oFx_2HFsGi9=(HyGLH8CSDHp9GTdQmcv?hkVkEURi1cGK;!RvyOqUdD zQAFqYV-ifhm*7YB@X|*N?9*p(*GM{GosihcF_~#yqun6Vux_2v*-2C0kW>>tq@Urb z$>^pcf%rMn8 z*5w@(1~vM7dB-_M@5?bovHU*XQ!*u+o#YGHqAIB%exGq;M(jB-dqBTloSq!lCBkze zcrt7y@0UprOBc%eG`OrlxhErptfRAE8A9eNrJN3nI$ zah>`&US#J^kv%-Wi|L_;Myq6{`Z%5FGmILiV*@pyOQed9(7L}vd*pO=%}B$Q7+n~} zfqqeOW)JN~dy!q<^8+U_n#VrLNv8Q`supdMBb@}D9?7sS&W}uRIy*)=aXKne@rzlK z)6b5hqW^h(p_ZtN)G~Fgx>>DO_o{leLB@{jp*E-|)jD;*TBGj7f0epXtx(sftJJ0H zVs)W9SDmR&Qzxl~|1>Iz{=ZSl|97L349*I%4fF^O8{r4#j+4kTNA%Q;xfn`u(v7v) z)|ECl)XHX3O|7#ZaYJJ#MuZ88c8t}h&;N~X{^v$FnQ=Vu5ng7e&XJxItvkj#$vsRL z-y6VlAMIzD&R)>T=@-Q6lq{_yq7`jof8%!L$=A-{SjVtS#Q%+r{>R2f$HzrE#*fn7 zBE4u=>%r|qrB@jg`kkF*no@7DiDus-$LXsR95*^4(x+)PDN#C(7eE}-D~YWYLhUF^ z6XJC5eb*1KEjy}9{tv7?Y_rYlYw^XQt)^Z}9P0iS?7Yq`c5Y)0!?%-c8*cxa+jjoM zp`2@f3w|wii^Kk*K>H_hAAOCS8ghQcU(g4%eg7Y_?LU&gS9<-OyqVngc?Wu~jQgD* z`E{}c_sJ3+GRLCh-*KUgb8c*hJ&vP8;HNFt9k!cp=(hF}>FP{H9(44-qwTF-WQOfx z-+kOPNzt98m-&tzOXZI|eCxHiiu?n8%tSF;Q~zY+IpWUe8fw@BxZ zY3*P#2|JE-rb-I$jgC`&@t+woJ=M4EQ+NY5!@cklJcrD%Fa~n~epc>h*rLvp5&YI} zJT3%l{%gAZoA5v4zlg5gfqt|8U!*gZL*#ao2Xhf| zO37~rpK!0N{XYC}gk9_WU+uQ;&y@nT4*7gWmto`=$?35FlX)GoizP{?BA*7<{5uXN zFE*?v_30Stq=!m!$bBLA$8tOV9rpjK`3CWJ$6w>yvf9I-Ezt<{z2Q$P}z3Krt@y{?nnvy&t^ZSfigztNP399UN zin1eIeIV((<%eDTx5*w>hO+Lf$4k074)boX?ite8#E0$HJ&y2ScX3-kaQYzkX1nR^ zf!`kJ`2%h_RH_~4nJ$!Yok+D@3(DlHziKJ($;F->?IQy z3b%6OxDWeL-q!qQMw+9!4=FMjFx!5GE$~X1I?RpmR9ME!b%K8QZQ=RE&2^-3ef?#m zvTbxq+pDVE@4SNj6?Fh*uzy=Kd~HpgMP53{?69}}6w=Y3=P6bKRgA8?xtVFL$v2Gd3w}>Nco_Q8Vsu$Y6qx~AGj;4%XkdbCG&&Qv!ek1>M{g3m!?@}Jd?5=#; zF0eA@@Lj?taex0UZ6E)8nXsM{Fn0%O@Gbg3r`7t0pK20!x9RLn+>4Og^%=a1_F-4| z!GwpQ4y3C~x&NSZxDS^~cGw+Y4pvJk+w{GmLyKIURO0 zin_i_n6>MFke2n^N*`sPg|m36I1Kwc{%#NXee!2F{rw-|AJA5&@eI5}-4DmtpWATi z!6R+Ig~;c=Tjuagb=d!_=GnyC9e<6#4`sQlpW>O#BW^sVwSPsw_7@EAaNy_aV#-_? z%h>09cXa;4Uwz0mq)~ohIrXA+-WB?jKitpkVt;^slV3I&yW0KZ+Rfvr|A$M0dbI5# z^%H&F8{}sYV>ZT#>TLSGJo=v;`nSQ1+g4)ELEcC1KjZ2DNZ#h@@AQAs_POrB=F4&` zc}~L|9m+Q^t`h(6aKd3zkmtI!HFvc?gRTyD8sqTcjKhJx=kF+J4{Lh@)7mXPU=;ja zoN;O!<-0}ts3$4=u$&$#Nt~DDgh^44lDBslk6yBiIf4BD3+PuJ+QMG+t>d6`*tGU> z?8X*`XSiqENAOyhyD?vY4an>nCK59XzmN4q8?Wt0crVnp_iTGzWitNGY~VgIk1R}*)4{EPVN zN7|A}%1ZN<5U&HGc*DzpanxgA z@7sjX+{4bZ@1w0?{%h_=?ov1i9%0=D)2u=tb8z~n2*#+rbPukB`8adwm+V}cvo<5l z&4f=QPu6}ib2;{$HCGd(UinaV^{RQRF8yuag*ajKt_~ zB~|^x{Kl5$J;0;CN%!r2eV^7)kqnI#|LJjog;9M zGpVmfF^-LfY-H`YHxiCujPx^p-6h%#4v)z`Wi0qi_8aY3-p(Ib(-}j)Z66cC z*oD2nCp45Bo7_TV@#CfhVo^{Kj0LivmSXn=5GsM+YszFFN5$uk$lX> zKAXIhV9rP0&JFFg*zuDknd_Lqb=YgkM`jk>%6)>+e88^H+4*uAWn;^w60Gb;lvBr? z)s`{i%@E--wsWxG%XpV@y)A=|atxX1viHoEm#SwDlR|!M*)k6^b109ysUt5@NA{;I zk3i1O+b?1)x&@Q_#rz`LkT2#TPH5b1$AjEYp0j9Eio7jPJ3c>0GR34tPI@Z>K45J4WRBHl%BckedbFf!97wfS>(X!0NVr zl|qLd=ZD50j58QJ=vwN}K7{usPgjQ9n~7ZS_xNSd-eh9FOM6qsHGR$A0TZf(X{|O>w=~Vb18Ad*YbNG(uMd%)Qz!R3wm@lyh0rV zn^)S&anvInDw8v@_eSnb^mL3(8Hd_3w*9Tej@v@z8}9o<*W6KV9%V8_%kH!drgVojoW!+wR(N-WLGhXtN0a ze&o;Ivom=1uBDu2+PpKax9z)aAH#hT`{%d!MRwiEwxhI(wtcl_6f@@=IKbrRoFx3{W6sPVTh@eq2?saxtQ}Y z%Xn@;KaCEXr;dKXn*T1&Gw5tU$GPZQig_yLnW5`S30>QPxJzY-(^ZB#@t8hl=a4%S zzf7(@#a_QHQ}b_RnX?b2ehi_H9jb4@ybN=B$eoE_CS{mn%aO7Rt$&+?N&7g`>PwoL zm=Ty=$m2rNv}K(LgW*ePh2O}}2*S7r9cz*y9mnoQhn2O2b=h+Nce2bM(`{d(TKR&8 z^)}nSshg=Eg))%!pmf$`M$*m?v}-ubA3}1hS*C{igR-_Z=DQi5W z-!m5bs_hHgZ`i)Y>Y(p=8txB;TN#U86R~{{c?h+uq)Ay0qz%-X^&Z+A7t%u}vb5pp z{I)-mbR4~mXJItsm*LbS)+(usDfH_OZF92ClZ|KlIvXd{4zHyBFK+uabiMYP{}&0B zp)JS1Ayl@ueE%0{FKgPcl(x6RW!{SUF1!g1gxfW}AKUFA|8KBA7UrqYech$s{3o|9 zTT911{BN4}zW;Bbj-@ZKZF{&c)m_4UDD#MpelgT9jHOLV4bNTaPZ^)2?b>IC=Ja-6 zZ|75XzVa1qrETZKeVEn3T-wgJU--Q*vU1^m!{#B>uN;Nkv!un@9ie_DjWpA!x7I{f zeGV@pvx)Xok?*0%lJ}4vJ16SErf>Umn{V4cpd&PY{~+wP{r*s%-Hv|kf6?4!OtRax zkx)5v-TxbAd%4@P|L^hp{pq+*e>H8{T6Q-_{ZIVr7o7j$9LUv_me3 z-*$4SQ#le32D(8a@HLU_8FqKa>+x}sUw=&K1 zzmpNR_X?ZVZSCFu;&1(Wne*s-Yw2HW>1S&xgIb=4TIm)xQ!#CL5_E_Ef8?H|Tm#ux zwzDpNxb(s7!Mbm>bwdhs`-RM@t-XhZIZqdh4YzV39^wl^*5E_(=%Vam8N=FgjYW5) zT^mpraZ0_yL?Byzxbm!!KBibV5+P5{Qx@Lq#I-1 zyPDR{94&)&f(&&`J0bT8f4J3p)?3zd{h0FoE_9Sdj)>TOL4F4N?nvlgTZ>5BVyFk~ zT&w;4&|Z6AiE#Nwjrm@Ls$>o~k$SzGFze48tv72WIm{tbbslZ%uI?Ne9nzO0ot^2F zXAaMReNNizZ(+OH_66o&^AszH7U77Eu zIq&hj+O#Z3hMw2X)Prnmf2nz`747SewclD;DcL;j>hSRaSJM`{J*gRMcM}VbrHZtB*!VVXhb(JawtdY7TUTw{ zZ5b7I_s8rn@AC{^%d^~-Y~~tTAqOm!gf{>tzTOqCi}d$Wn$X!HtgduXMm000LQ_55Z2Si$Cox<+dlZ^ z11l9)M~9w}E}K59o;z*cI`TOXJNkFlhSMNkTX%@WF9{BSjyzkt&CgTqW=O{71=?(W zZF)9s}GFwVYjgv#kTbh{LkakD3 zA6L;=FTy+?b79DxiC-pVkpe@MOP#akC&YbCx`*keR?xSvpr5;rcJeyRwe)Y6*GS*; z7WKR%Z#G@}ozS)2NSM9GnQg0C^K7+iD%!58vR-cICFn{Hh1v1GT^k$BJv)iHEHU!g7&B(n=&ek?=&q zlNJO1Noz!s(bElgN<3sk5lj>5o(j2uySsH)LXAi&?o`rE6G(6s4r z_iPnOM^E||KHA0J2fsek09}31)n^sd19AJb@TGVRTl!6B2Vm0ZhhM*~>|L@J8W>!V z@2u5)3R(g9_eb9z#2=2oNIiXc# zB56!Q$G+9D64r_ANBsRAk)i+yD|@dkw0`f2aP2IGKbg07HAQfmMd~3`8p~U*1;y&CNg~uyNs8L%p}cOn6t=VSv+Jz zAG`I=J;t3~FL-rUX5EOM*^q;*^oOasEoivfMdX8}5n zC%t(B=$ePFd24}u&ubEyzf`1xcoo~(Z=oEPh%Cs562QH{x=C}vdXa@2L@FCaP9)w* z%_1k0_Q|P`3q^qZ$u&?14bUu7g-jLss3ISyOcz;19u|>@MUzBM#eFJysa^$`r)5JS zke|~kp+@BNQjs$}pshvLqhRy9B*U z(7Ob^OVE1`^5-;*oVyVkMb0A+OPc_{^XI^NkqdG~F5DneQzmjz6kuLl0_Cto+oNy2=U^-MmEvyD~z1%4BN{h&*Eh4Yh1L?g+*yb$QF7n25kvA8Myj3aE7$x#H zy561yr6TWSK)%Skgf|redfwXz%_1Lc68Vtu4+;OU33iBlMEsBN`v|{}N<==c6WLlO z@<}|b68Ur*Aop1#hgxnB`2zVbmx?r}1Nr;P0rK@#fymd$ep3j@Zr>*ItpM(C@&C3G zYG4)A!)B51Hb4`!ihNIc-)95z-dY5tf2`GAfeR{>!^Z53%LVapWY%MC5m85g@a(nlU7PZRM~;lq`d-eAn+l zBX4u97G?6;z9Wad(vrcNlh|`=I=hFYiVBbm>R~f9vo})|WB_p@OGI^A2b*A<@L?^W zyK|GMXyQa;b|Jkkb)sVU!Xsu5EQVSjUB(Y8hCIX)rz`2ip*y}>R3c^~X(Z;0N?HZz zO5P!=8~)w!?}mR$4B(fVB`OX79_a768Jb09! zx~PM4VXLTvku6y!>X2+vQ%U>K0#T)t*chTp)Zxe-p2rUNS%Ci$rBDIrJObHiO>8Mh zyrbrbn!XOm*Ys_om~yEZ$v|BCM>V4gmcu$gW(G1dk(rePjj&x*Ss;o&Qq3kmN7srv zCLJoFS=1cv(VSGsg(7GXHMc_4vBW=irKsZwJB~2=OLbfsRKqe+<#|vHHS$t0K#AfxKKwUM@xVrE5i9whZb-U7ioqfcz~(=M{uqK{{7#7j445F zk-2g)EEjcE25b?voVd%2fVfw4ZC8`d)#UYR^jw4gHOOAG1~vln*R+VbmUOSJgALFG zJ4CIB0(7oG5B;{f9zEAvlkn>ar~g*;-|7ag>jwO9sDfG`tsB=Hlw}$-OTM2~SR}6DtF_5SGi$tw;pi$HV zr2F79Q4g(#^`h3He;w|1b)p_F74-;mkC4`*C9qjkJ=a*j78*o7wo%mMm`{|8THh+_ zNz5ln_o;MPF6!xQQ5$lAG&j@(@i#P!dL{<40QWO3qMof1^&D}Y%Y$MduNx=9bRcYF zt*GZKMZG}07YKiW^j~NY^&;Uf68<9L4TLqIv%%U|i+X9PsF(43nRH)4_bb(~Ow^`S z$b}*(1LQWX6!j|lUn>^%dIq$J+Du%=IchUBY=G^e-azIJ{NEt{8^nKu_;2RG zBp^?36~JOqjl^%91M8s?i2HT`>ChzV9c13Yd>8k-c~A_qUJ=oX@t4+Li^2MSZ@6?TeO*`jRxh+yaC(F9*_Rtfjt61>9d#4qtDAZKA#rAdT(l z`Zj=c$P@Kl2CRg2q8L}H?@ORe)DH5nqa3QC23CmrAqKKU{fMp~vHwVVKjHp~v|7;D zg84H#f8HYM7vz7f5Y?Iu`NGRdXoT&edjx>J z2lk%Wd!|DUOoHi9B|05@I`(vHuZ7isj&$PnDuoKsy~%f0MfZ<^blAYljdMf~;2H)rL#t@UUpgE4f#iEot?0qUqH{<$r$Y1)?0XV#&&9A@ z^w1bUM=tJM+_`H+4=WKpoN^dW9)_<6@;3sRynG-mZ;R-WRX}>9YGJ$Z@&}-6bOqp_ z?}*-uyzi9Ql1=25^E_y-+kk$m!nothaq9-m9 zy$^Ys#I;RI6}@jDdcU=z_n!oO{}r8fTE+Ou}cCLIo^^m7>ckp$1k#J)pM?*)r0c9S>WeMfA}zkOc*Rj-%0W z^mft5kk@07nX^LlT&`g*=^i^tG~+jYT%+i6{EuG(%S6x11H$Gti=I!K74^KlR{~{F zDSBbN=t=?7KXH}tW1;9OORMNpR*PPQ>>_lYO8TdgcJ)$N4un;&6Mb4P6vAS_|MV!R zg>9nGK+hS;fO~N<)WHU55`8AJXOgG094LossDTwg_*pirRrJ|WPywX7Bpq^KCA5e> zhjh=`B>G(Z&#e`G9Vn6uqom^cB+qJy#Y2dapwEs>RSG zdU>_zt0zH==xa)#4A6Tmd0{N2SLDD7(bpx5zCKIz4RfGM^hyWNv66U`m^g_W=lHo-Qw*A+;H9GC>tp$e7*a?dwHi|7}U zArDHS3Tk0BtcOO}F8aj)(gC>_i(w8dhUKsZHo_KY5zRPGGmg^@1yBkVuoPCpI@ko; zM871E3^_0frb89f!fIF#jj&zx%K@ZA9+U!dFE596&l;OHE*Jd<;cqO47SV53h<+;u%0xG2LxbqI@p~r=8b!amT67b-n%0PZuL?GZ zexG~(K5;)F-UrBifc_6kfp{M*g_W=lHo-R0TLegV%NEfe&Jq0)@jfE{$4#QQR*U|G z@K2Tlb?ehg(VxYO-j*u*bJG2sw7$Up1#(}aqd6IJpa6iVmfmFx` zZ%d#YmcR;F3k|RpT19^s57|%%)1VrV`>r0EpjGtusgMuTpc+;{J#2<%(K{SSgE_$Oh!v z%Ap48paGi2NEBp45tPGXs1>6)r9tHZZq*3epoKTRmcnw_D#m0$E)>ElSPMJEFitj3 zB@oXcoYyGPpb46xRZL72q(V02LlI1aa;SzHSOIlV4-L=+&Cn_)HVRT98}gwD zra?JWLk+BeI;e*RXo6;F71K2eQXw1ip$MiyIaEUptbjVGhX!bZW@r@?7X_)14f#+6 z)1Vxxp$1k!9n?buG(j`8iiwYcRLF*WD1vEF4%JWtE1(YQp#hqp8Cu08L_sQKLp~J2 zG$@B^sDTww2ldbZP0$RjViKbu6|x~8ieMU)19dvFUQ81HNv&d%v3H|xblW5*g|P0_ zyYAGX)a_!JQ<)xlVtS&dXN#Ehm4Ml6m6+bd>D?fv4`v4XGnR|#TO+1lE;NbBEESXG zK(m1*;tts;X3uyrLy14MPE0QG zhY>bBfCkthW<(TZLlKli4b(vcG>gfLf@~;)a;SkiXnpZvuY5@O((R=U~F(ny5 zn*7#mO7Jgf7IR1r6a(^yEQS@Z4mQJfF;j^%wG_}bbvg+cd?*6+ov{?qc?Rh(M#q_TV$Me2 z+2n7D16;?FVj#_Pu%AP^=L*CCa_3e6>7Glx^VY*A*dk_W6p-H1Y#_a*MPkk;&IOnk zZWmL7jvB&hNc$qvxu^!#!A58mb8!~rK^auS5?BVLaWQFJOd1!X=MwZ>g5FEgAqNVe z9I64Gm#l!TVrmJiMNe(Lm`h3T(qbU(O9{IS`(^lF9s^}SK9)tnBv=CLp;gQk)ldh> zUP=D=&A?nqT34cx1;-Z(!U+K+vh-)m^-lFIUSIxi-9I+hE_3mk>9&gVV#(} zk-2*rp!=Rms28(_@HJ(yLCn3yfZu&w!+qrczG|p}^VQ0K z7Qlb=5OXKt`+n4bZ8Ot z&KyAYUDABFNla5Ipp*Hdd4CDpB<5H0-MT>>6(f$5E{>Za zPUNwoc$;w_-YkuiX<~Ps%#d7RnNY6n#4Z~)lz056hD@wd4A#&7jlk7zw|jzfMncHn zNU|ImwmW=xabno+au(~^VY`RDHf;Av@4B!(;C;6rxr2t}3?Dtdq;T)i z*57&$wEn*frCZn3*$Wn0Z6WEuk(;|PeMWlaf*EDAkDsyNxb*o)|0zr3=P#(3zaW&S zLE~pESTG;)4!_yy`^-EjyBn24SQVILb~q3n%ZIl6KQ9YQ{a%V-Y$DB()>#?~Q! za7h0`a@Zl?5n^?>YfAq;Y%1w5KzB#p{?OeKt0T_8);AX&_R7*JDVysu^d677K#s$2 zKG*p7r8J)K1(de^YsR0e^Qp>c;ogcT276u2T7iAWij9Jyp8urTAJ; zep7vZ|&xOukvnQvKB)^0^wIveiH}NDWpwYKYoX4OO|4C9@Sr7|^$DRC#Ko z8l^_7e6^RnpvLeG&Woym({=V%<5i)W!0E30s7Y#HwV&Ew`l}-Ppgq(9YKpX|Vs)T8 zNFA(7IEiN}yYL>WO4VT;aCd~7rjAtq;Jdz~q(M#R<*%32OnF(&l3!GryrO1vqVF+k zj+(2ERmbsFemUP|3{vydd{x0-ODAxu&qB`gIg$PIPgYgx6#0m+BNnMsRW)B|oTg4! zXQ;*MOm&tzo4xtZVXyu3)KYc6x_}p#j%Hu78rj5-{THiCRIR+qe*BlI%h|vG3Uwvl zFJ7gVtE<&D@;dwZuTa;i>(vd4-c#Mg?*6OPE$UWv8@u`6&TBE3sypQ#Rj2NfHGDI2 zx4K8I;j6)1c6Ax1?qlEowdw)&pn8Zi03K$S$>HizP6l{Pw#u*SarJ~+&*=b9si)bo zYJ+-4J*%Em8`bmb1@)q8P%o*M)hlX~dR6jdvwBUvt~SdX>J6Er-c)a?M)fwkd%UUM zRZZ$W^}hN*ZBZYpkJQI%tNKKJsyw$WZ9?aQ*LwHDb>OGand_7d>>S21gwz~%IBVXu|dXyfml+M?C z=`nh&F3{uj-g>+))D!eXy^o$GlcYlLtM}9U%K}}bC+h?B6kV(jcjNm`UpKuAF2PLkJ8ii3_VlN(q($KK3X56=kOiQzWP{pgW6A*>*M8TJx|Zq z75W6dKrhsl`b2$_K3P}kQ}iNzs_d_;^=bNaeTH7F&(vq>vw6(ll1jZqpCc#fbLC`x z9;fr3FDL2?^o6=cU!*VAm*`sd)S9d>)0gXI`U-uezDh5b1N7DU8hx!^!JZvc^!54% zy^`}}Z_+pGRr(fvtG-RI*0<|B^qsm+KGt{f&EMVn9=%52tM8Ko_5FISen3B{AJXge z!}<~ZsIJ$K>BsdGdOhQ{Dw_OL^i%q2y+J=Cr|M_*b9y7ERlcBKlxp3eU*aU@S7edi zq+ivq>DTpU{f2&1zoi@X+xi{-u5Qxr>G$;qdW+nsKa|t-NBU#ERqmC8^(XpM{h8jT zKj-V<68(k#Qa5v0`B(aD{f*wPzt!LA@AVG-gZ@$fq+9gQ`WOAHOx3OWH@#CnkyhPi z#3&BiGjcwA!@I_lLpirF;CPZq)5%1c&L-M)VF&+M)75b3k4Z3zCdnk5ZYITaH>px8 zr<*j>!}OHJCSA@ny-aV@$7Gnka)#+=GEEjo0qkK0m~1o93^IdFju~S1G(%0U9A<`@ z;p}U2xXI&qkWpr|$v1nMF>(Z>{$?{)9x(-GoY~uqH-%<`nP~PgljKOVuh~yNGy9t& zGua$qrkG-LpgD*gPS(qlroE;Zx*qmw3GH075 z<{WdbInOLL=bH=6g{H<_WG*(Bm|AnGxy)Q{mYFNemF6n5++1z0G1reGsWW$(yUjgjjk(v{XYMy^%>(8^^N?9*9yX7dM@_wX z%sg(MFzd~e<|*^E*BfBAiI46X!d0cA}jwPK*=lbampK zcn)<+v}d_E-JBGsyOZjqIX#@7PP)^}>FxA!GMv6nKPS`4a{4=aI0Kw)XP`648SLaZ zL!3RGp-!$d%o*;CaPpjy&M0TJlke>1jB&;~1pEGsP)(4s;H34t7eML!7Ccqf_b}<{a)E;Y@RmbpGKSGI5VADPMI^?IodhK znd8iLj&+W6%AMn#dCq*N!a2cN;4E}1ofDmtoRgg@=M-m=bE;GAoaUVFoZ&2X&UDUl z&UThK=Q!s&=Q&HA^PLNv3!NJ0BIjb~5~tR=)VYiq{ zdDVH%dEMFUyy3j*yyY}HZ#(Ze?>bG+d(QjL2hJAfL+2ysV`r=LiSw!RnX}FL-1)-! z(rI?Sa=v!Hake|(I^Q|pJ3E{ooFAQ^oEGP2=NIQ!r`7q*+3B>o;wo3W#&ulR^<3W# z+z2<)?c_$eo!w}+iyPy{x?MT`DBewQ6Wt^?+3n`0xZT}UH_h$g_H@(TUT$x-N#qOEzS?<~H689YUT=zV8se8VAfqS7_<6h)m>|Wy5x|h0_ zxtF`k+$-EG-K*T??$z!!?zQd;_d54__Xc;Rd!u`kd$YUBy~VxNz0Fq^=|1H??QU?Nai4Xc zb2qxryDzvex()72?#u2g?k4wD_cix*ceDG3`={@Ho5P)@4FwkTig%b zkKB*lt?no8r|xI&HurP)3-?R6+5O7>+Wp4e?tbfj=YH?*aDQ-rbboSN+@IZF++W>R z_cwQ^+h&i5XOm*%IUH5(dA=8T5niO%iIcTDd(mDOFUE`Yx_WV5yqDl5dP!cg*Ud}u zx_hZ!n%BeY>7{$Uyxv|PFT?BW_46{lEU&+}hd02>_6B-`yun_MH^kf18|vkH!@S|% z2rthY>5cM6d->j8-WYGJSKy8F_V&hmh28{jqPLGX$=lc4&)eTC@+Nx+cvHM$??CS$ z?_jUQJH(sn9qN^Ohk1v4M|jh`BfWokM|sn|8Qx59mRIJ@_Kx8*f4c8Ja>{N8>aKf~|q_wzITEWf|Mhd;p2 z_6Pcd{K0;XKg8eDAL{4&!~Eg?2tUss>5uY9`}zJ}{uqC(U*M1P_x8v8h5iJ8qQ8$n z$=}!C&)?rK@+bQT_*491|3LpB|6sqwKg6HvAL^I-hxv#5NBGnHBmIB)NBPtJ8U9Rv zmS5)2_K)_D@#pw+{bT*({Br+zf1W?zukcUs7x)YPO8-RvB>!Z;%0IT1S{}caH|1*D^ z|GEE#|E1sTf8~Gef8%fWzxBWKzxQ|eKlnfTKlv^G&;Bp|uYRllo4?a<3)sM)ZEQ^7 z1gU3sQpaL28f|^ay$e=|Qic zchD!u2>J&7g3KT*=pXD63<$D=fx)0)aF7!W3HA(z2D!noV0bVh$O}dWqk_>vey~?C zCKwwO1ml9egYiLOFd>*2>=R51_6_z6_794J$-x1^l%O~`kQL*2NsvTIV#T)`y9RWZ z)Zn1t;GiTpB$yf;8k7cy1&0Sm1k-{egMS1^1=E8W!OUP*P!`M%jt-6q<^*$tV}s*@ z^5FPjUNAqX2u=tV1Pi5}H@|lTCk7|U5AtJha!?hV5-bW%4XT6Fg42UDg2lm^!CArC z!II#d;N0N6U}md0Ng3ZV&F@6ZtN|opL8D+Bb_A)N$@#Z@Eca ziDWN>uEAZw-N8Mwcd#b7H@Hv!A=71y6a@DNYl8=Z2ZM)#b-}~IBf+CVy>ybO;IZKG z;E7;;@MQ2*@U+}7_XQgyCU_=zHh3=B7(5@m5WE;PuyA+>J2E^W&&XPNKptd=h7G|> z!OOub!KUEV;I-iOU~}+B@MiE<&?rmfG4X@9gLmZC;N74pcrSQA_#oI4d>DKbd>m{I zJ_$YzK9h5TZNcZk7r~c7bMRI0b?{BFJ@_{GF8Ds!5&RJR82l8p1V0DA1i#9aLF@nP z>`TBSJC5@P*8u%+EpJJyV^$z3X%|?{d#?u=NR-66-ilg^Tpnuzlx1NS$1X%Hu*AVD zmtx0`vbN;JM`An9El0UCpe#pn5=C(m#kudqXKd$mPT4uEoM!&&`n&sKmx{m7q844< zue!Usy1Kfny55`0XDj^){Pa}i3lpyyeAvefn-McwshKfTt(>WRuNkj=SLFvQZ>xMy zOF@Qu(FIcbZ9qUv@MzX4afCcbGfPUFL3ckGa>}XWn4mXx?N_n>U-cn75j@ znVLCc-fre9-&dKae5EpD?yr2ed7$zGl^?2nzj@HS!@SeH%bYdm%)D7J=gqrK-J~Wn zxv{2U7R{1rnq~7I^Ir3id7pW|`G9%Yv`pJ{OxN^GVJ?^tnn%os%tiC4`LKD+JZ>(T zC(M)PDf6_sY`(!fV^++ndDeWye53iO`6lx*vu66{idnCG(QKGavt_PUK34ffd@IUS zK3Vzp%FW8RRlcM0E#{ipHXnzx^_wd{W}Y)U=DOK6d*+6D-t3zLb7+pt3+6@hl6l#@ zVt#}9jpjF*-)w%1`Gonc=9|s8nBQi;)qI=z?dFr_cbJ>z+s${FPnl1f?=-*D{4Vod z=DW@JnC~^e+x#B$edhbk518L;e$f0r^ZU&oFn`eeA@hgLA2FXXKV<%>`KbKV$x^8JJ`9s`;GxVe@(OBj(SUFPJZyA2om8`~~wD&6mtyGG8`-+5DLK zE9S47zh=H-{<`^b^Eb@jG=IzdZS!}`-!*^F{C)Ee%s({$$oymTPs~3x|IGY^`APFr z=BLfin4dNO-29yRdGibAUzmSs{+0RH=HHlqTltjv#mY}te!=`Z^Y6`nF#pl~C-a}p zSIsY(UpD{6{8#he@NJu)sC>HeGjN^$ocZtOS1LbM`T5GvR({fa&HNAZKh6I#|J(c@ z^MB2+8hp|v8A^tekz_O(OR5Qe&lbO1o+OjWR5G2+B(uq>&pJ-3!CFdyW456)zh@S$h6KhV`?R>r=70mTP}}!9p+iD)nvZq@UyyW zr=E6Q`}HjIcr5R+pO)*Vp4DBNbr!qht?T{u{k`4s*51{<-OcBc*8cYH)&Ba?p(DAT z<*o5nFXq!8N!2FOUY#U2(q=1WOuWACuPj!PHp-ZJ{;l8!2GYraS|?f9;it6kjUfN00mG(&cOukUJuAjcD= zj%R4i=an~U)3>U;NIQ*c&rMOa7a9cz$>BC$X)o_gJzAlmB%R0!&6(H!aRUY)={VJxR-*>M|eaNPE*MCCin zl$I>ZW*$GW_NtGUW}@qxax;?lx|HuOXQoRz?~vyQJI=!*S zukGz~sdhQPUWs&xrFsjJU7vpZ+7a~6{?YZF{*mK$Jq=o`H~u)K!7cm-X={+{J*6e> zk^9ZX$;S_N`Uls-6oa4oEX&w)gX3y&MpffNd|K5fUA5|yZmOo9yk&~&S<0a{$b!_7 zgQ~7+C|4_MK~gtm)u&VvH1U!kvyJ7_o_s0_@YGYc^p*A&+1Db6yv#MG${MY73nE{kll&GM#W4cJe;qnj7TXRDXX5=5Pm61_|GuBx%L zOMTmIsATuZ@!sOtRdu1I8VLa^yS+mBTE;@FH<5em-RzC9lI?1SoN~g z4%+b&7k%5$Q=|Hfo07?ABJa&J8{3=vn+Mwm)n~5m_jTjWI!jt7EtV!%BeJ$z*y?IP zt*33rkF-~CrR3}`_3UQ3rOqgt)n`>QH1U#=JbOYpr@du1q&gQJIfg>_y2C;}Tjsu@ z^N&0g0hLjv^Nw;V-zA-}Y>5OlDKAY`30bqK_QO2m3_=Z|+Yq)lhlZ5nRdS%)Ij$Dx zTggLxCl^Q^nMg^VS7u$3m3cgBrXKrgQO@d28zd82pidd-^9<8JafZPPbfnZXD2o23 z$vR2JWxCnVc-TpmPOK;D#gxx^4oeHR zyO>w6Iu1={90+)K7Q+2L$*eX7T{rbyiLX=7UESZ@ z+}%N>+V;9@J@v|NxmVpOt!V1JbeS@-*s1OWsm6NmPVJQ1C82h72lJ-mCe;}?!weRZ zQx%>MixgHp@M86lPdV2KPtMsAXVP!Ni$!%;wY(-lW9;^C>>V8L@7=hzS=}v>YTHx0 zx0C9o8K)SZdLA#2Z0=sw;c%bQ(v$i-edy?5JgpN1Wv7~i8HVs5ZF z*oxl~mdKT!(u8Jps0};0ciVZ8&oZC-w#Z@Cs`bnt9$F1Gb6bsg4$oR#PL@o_m#h0x zD6>Ud-FGuPwSU`;rsR`Xf=kpCOXTAc=~`mbepgtcl3b#oTaUL~Jy1E*#LJm*0Ow4v zT3eF`aZOGg+(H)4n^agQ|}L63s!W%pqS)AKfO&SqcgD5?ofL&DPkFJGUMM zMG8JrzpCQ`XHhpd7*D7AqMPc;7vo%-7sL7W#c(J^9Y2rC>$F`=z8ouwa9~?(R9_BN zuxB~`pjcN^ma~bJ(`C6<3r}9J`ie@PCS}Qk#!r)1PHc|Q@odoVUEU9(&N0{-^^vWQ zZR6ig0{S2~uyWT0#1U#afXnbVv2+=RmIK8f5kPT8tk0_W3+NmZ+RXA|z-ooYvu2&8 zYPz|)1l^j~x^3xqw2waq-W0gY9OUGS)#b<>!1huEJ;ic%j-^m297m@IaHb#UCm@N| zaY$&}6AUo6-SUi)^R;gf(cb{4`d%`8dD`{#;k;LB!_$WYvVf-Vq=L23uqPU)f(SNT zEuO>$mxpnH6Ye|?U^*Vm*&6sNLo$f^#=!y5SYQ4oJf;&%PU-+huP`qsb^j=9$Rn9w zXe9wl^BZOo65|@c!*#r zJiyTn{HbmpUPEH^R8Iss*lVG3Tyz(iMWl(b2qz6hmAD115eX#0JG)HHwN7nKu6f<7V=dn z9ZhzSjBQC~Y)jx#_})C+W^_wvUz~~bhHqBTQCv=7&I6pUV0c6 z`5Bj^e?Uov=9!WKz$b9byBb37BsA^L=D~r$W;?b}2E%UWh>YP&R- z!dvtpeNRcNZo$A<(DIH~TM59~vc=%?C8HsrF1xBSn?~)1_(Zl0qt*fo#b5w9TecaH9~W~W zqIiLW>q8nhJMN4LX!LqS3X+yB3OKqem^1~Ti2~#`(M-uMaH!?w3j;tnI$Jgnkk`qu zeoX=^l2!<5RTv3KCN+thS+*o=LUpJEB|<8;_B?s>-S*KO|fP_*uef z#_!T%)`V)MvzlA|t~=N@0ijvqdudk_aP$a&%#l~(N~t^l8ntFZBV7@TnWNj57YLN* zr;cu2Cop+pWq)ACz7UTW-8dY#ADN?*OQt7lal)BHYzgZmF5trRNbI7B;Kwc25lT;; zFp!7_qII3CndC~tb}`|MB$OGolUPSgC4i$ZFb@;S_bS$sJbDp-j9N{AW=e(=vQxo! za#-b|iUoy2PMlCZ`jY&4SukJrlQUDYsqkRxg z(_4Ug)}{-@U#_UvKC62_hFX*LOmF@$W;z9Ushz1O2<@m7p^>SlqQU#c2JaUedM{tM zdijP_jbY2nH%tGB4wQE7QQWohkOMod`VK3Lo&iN>{JnY&c!u&G+NmVPd)(FDL# zFM;JfsHKy`@|MTaO18Y5w{$97-pX3_>ep?ijuo3JKW$z8KbDs~md3HwTM^)sPR|GR z)7LG%{+4{RzK^uJj;@hv^+mY#IW8+}X1ujQqe zgDDJg7Sb;pc~!sFt38%k^Wq zek|9I<^74(`<1$-pV_Ma9eMS(SbFTO-pAK1@8K=IR+e5XORtq>B#_m}pStC`vwHiA zJm*>OU6H3;@b26)$i(V>Y~2==QwEn<-fr50^IEWeq3hNbD#s13vj*j)L3wLX&Ki`j z2IaEB_0-@xXmFi3xV{@)#|^I6MoN9b_1NJ0YjE8)xZWCmozVko8Ej)UUZHN)UyYRG zq_@keZ@F$6Tw`;}bi6t#>rvAl1vmUJ`X%JTBeY8*-3>Wu+Xu4~4XScb`1-n`g`Z=dsMNgrL? zw&BO&>GpgeU+9OnynVFvpIctuS^BxH-hP7~_NTENb;}zsOUJY21(sbT-MnnFyoa%L z9$Pw#EuEv54o|y8`gkW`=}fhBK3d+uSzg9k-YZ#NBw5~6Szel3-q2azMKrvArt{kJ z($La5ZFz-k>4di2FRk91*Dd!y%S%YheX-&7J)PfH?_r@2$S>~8mhoqnx4D*)Q?|qT z(KsB`xSQ_dHZggq>uY&gZpiR`(wlJ&%C9#8Wn+g=Xzn( zgw^;e%tyl=!JwCTMibQ(L9w8T5KeB;QK2+?!=b4{X&%fA+lcK(I}=(f;)W9_bXP3( zhx)p0gu^J5SVRc+gKY%+0op7gluBa@c`qXPlP#1&B+|yg+c|!y}yBNEORw`6z=r|g;P^7Umw!0MDbvBo>Bw7>DtD)L*#C6^` z;zk-Ie34SU9BF99u|@9*^liWm*B*6f-B=pi4Q4kGV!Q6*MrjDwFQSJ-3(gfxgwPli zazu#q24}fw=77+NGw;0e+E^!wTqg>Q^RE#hPIqvKK_kQo2wpJJ+o8AMF+*vf7YaKd z1kV-Jb7WMo$Wa;x6kXy_*`bAa%>cz6OCt@=7S&M{k%l6+TLkVvmHQT;%_A7vg-(y9 zfnI3#SQ^@eeh;O=$yA4ykEO9)dK6&ZLET4$*lx75q4xtq7U|7GyVhMR>WG^q+(=n; zLSi$BjY-Gu$d@k_W38PV*ZQOF&BOlKg+9Wv^#N>~-VJ;_Xm5A)Sv(r);W575bl4w# z1cCBn7yCDE^bxmw{mMpv_`^rTj~xxKY$GyuTSBXbFKv%Jd2Mg(sqL%R`y)^HkH#-+ zQzMtIZI5*D@6y4xcGFr;T>poAyL$&y?9pW=(4zy4bg0kSzGm2_%OsCq-@HmBLH9n7 zSt9v72d#dBoOWJL`!1&)lhcmLX~*QWV{+OtInO^i?Vz0Jn4J4(PWvS1xgzJeBByu z?LmZbv%JZE_;x~}_5hxt~$U}Sx4Jv3`RM2xy1?@N$r0crf8Qwh_erX$#Sxyg)>|fi{C!K8Uz$v3c zD5E1PqZ?2}>-dN4*q-PQBkihk5y&fzJIWb-%qCoBmKl$J_bqv?L6Tf1alI+%2CgU^ ztjkPbm=2)EQV(NJi!!GrnKL3QXCzk6NUfZfR8C7OrzMrslFDgG<+QwVdUkSJX1PY# z)pL4ya(ZBLT2MJXF*z-%oR(BhPfSh=D)*A19vEmU4e2WQ;#R?+=R!R)c3C+lHsHX{ejzc|7$a5TwNiEb< z3qE)rpE7W5Kjmdf7TRa7$I19d!Q#3dp^&|n`kB6FD*I!zwCle{@#Tz+?XmS1i;6g>iAG(De>YO#bS6M4tT7lL$00`%}*m z@_eoyB;?5-zAsUzXBy=kubyPEo_Z5bVafSXlr_=`r>_NYDjPsTMJ7n1xRD|X$>BQn zE#;z8kxn=z4gi4=a+Cz*jBH>98nQUYBs3DmvIxu}#0r-qTA3VI)z*|N4r{_b+#sboPaZve>Od5g|R(S5fR zg{tM90C>}JGmu=|50INJZwbgx`dtCA4m=vk*c)PLFkY}j#M0nRz&;U5qveL(B9@jA zg5iK|BO=5uTnvwr6HP`Ojw5cQ%-s(YHyB0OW&&;z+%DK`VrgtFnt|ALB0{ihvG>GM zKVs}8zFmO6U4Xt_fSyQz-gE}&y8!5k1n38kl^EL%bAfddaoZ6gSp7hVy|p6^@e&q0 z7Ds_iDAL=ExFOt3cCUyV>1{?Dnvve%W56aAy9j10N@Ewnf<Lc6k_lZH%K2g zt=MibTTvQug9VGy*lzHu$hH>-6kHzYE#kV+9;G2TH?UhpraFZ{gjj7kQWl()vMGk% zg7ZkW##kDe8lrW95Sbcd6pP?9#pW01EstG|!aF2EUi=qk+h< z3Q-z64yzEQkvqY0BRgwoA-FkZe~qQFTJUWGA+lu2FFnkV_h?`zjAL_A8#))k^DA3# z=pr~1Q5t)55k{;Qdkd~h*^EOAORWg{OUz?B%gEcF~Fh3QkJd*8^_wSIX`lOCu*+ zvA5vGL~k;98XfMC(`xIX2(KFJp{Gl;wc9QxUfJB=JHL0ZHIC0Vxb*Nvmp6yk@UoPY zOl<8P?JKbTg5n%(za$0b0H37TbYN>Wm7jGr?~qr1*3}_~yz;ZoK#)2wMC%N=sOyC(0*Z8g>S~4|uk%w^^9FgHpZe1B z?B+}BINEOPy|{~yz8`Gf@%+)|fqd~kl;{Z3#Tg9eihR%gs#_D&r7Xl)!~{18v*l9A zkd~|e3FJF}4btAs6@*2~=kyOOh+=6(4TRmUF2uV>|p4U8Gl(7wvIu=0?_^Pxajmyptw`30&ibZd`u6+@D z4q!Ke%K#@sw@~iVD;m^JK*n_JMn>T~5^6(N$QbP`6iat8gAQW`T_Q6bJ4J57bqm0a zHdR+lmZi(n+q+xayW58^I}vnGp@Ngm=a2e3su-_t?;ahfHoUPzXdLnS*ZYUpCN{6% zIDGkF^H7DNdC@9=xieU@W!SfvlfII}p&g4%{MxKXiZZB_#u7&-4+9 z%oiDuO*Xb)*v9Kk08PM|#On$2qY+Yiig0*16~>9D$Q;TmGx#+Lutr|&5MWw>P+?|g z?<#)1V+X6ib)!^DQ_m?VkrRx9tOh^!>v;%_#iV<=7h|M=LKMjpj$YBA1J@$Bu7v( z9eJWT&lTj1@&(XLi8IK~mU)9b#oR5;$R9kIjvPYn)CnG;U@CCQJuUMI1-fFS(U}sj zTs6-v$pn50Vu@qOj?1sNym)j&6~ppU;__}EU{TY!BH83wvaBVtf3RdxORnr4UQ_7k z#s;y~O_<@kQHrYq!#8pg z!3x>j&|cJuou%nw^+>E&=QBj7x@L0qL1($%(jnE(bG^;OD~G+wgUxln-jWSy3pD|f zjs8`<>EPC+f_URWax?wwm?^is@ja;==WDGl3s7qdq?JK?tSd6?ana7C$96FBrTtP3 zWM!0i{oI}L5Craa+Ir?cBEYMTOfP$Z=Y|bDch*Ddc#G86S4EvMcr{}XS{5#W^?A-9 z;+#Q%nZHC%my8S9$7}CbgfhJH;95rNPNnsnTS1-`^1Plid@N^pV$SgDoZ*{!=C%;j z>)^dzybLTYyDiEcnb2H0(j)TJoHO_^XAon~Ai|u%z?uJi92R|o2ICs+RUE6L23-aeB2w7;2E*}|mjmj_)I z`#L_b<-j}2-$-yJlYH?DAV(Ms4d`21CT2f~QQs*2&>NYr}Zi7SWHZG)Y8^UKSdLl2# zd*32J@1+IkeTx9SLs9B*{?ab5p1TYx=`yIK%b=1jgG#!*;_vc`zssxhF0anJy!!6) z>bt8xmtI{%3y?bYAa&{=X`|9xhuYFZ>uD-|lmEpU5`LJMp?d#xu z?PO~s=Sq0^xvKfvpmKSow_2-R{6b~siHn2kQ_rpp?CHTfSJ$>`H!rUYh9BsE5no-e ztgp{qIeq5Lpt3rsbmqFRf-jx5_QGIjeo$N6S{Mw^*EVW{FJBsrKKSgb?;J`xz4hLc zA6YpwcjolXmD=FarIj;-hgVP622FvQtE;tRMe1)1-iadSYJ<8wuM6gvFRj!-#La$f zkX%|>LrG1ZB?2u9w77P9ZFP0^^kC@R>gwE}a%p9Ab#-AdGGFV}2BQ!3LC{#|(#l|L zu05#EwL$*sU}$Y&Fgia6x@sH8V^`WWd7_=m-@({gZ+$Rw_6*89wVSn@X#2Q6_5gyg@aVJ*iajq3#^wj(opZ0^hua)$s>sf@=fL8*c7HH@Wot0B zj{XN@XBP(J^EDw7pAY`RXypn}K<2}1tAeuDbwp0gzdAly>GaxX&je!p%YLG>G(64n`iB>vy$_$*)s=a6f9Hau`~<(v--|T>I6@ z1i!9+=Jec|)w5?72GjG$!^6G7M!&l-n3=}_YPG>s=R-0s0CVlt!L;O_L~a_ng~2SE zIptVV0|(YY_F$&7R=c@Y8_a-d3xiYh7oT1^9^L4!-anY$oO@|uaL4?`Csr;#rG=-@ zp!_aZe&_sgWwvv9<#=|sGZ^Z(2eaoyNFloI<0<(wjX#5-dodFu4_sO~7Rd#(+BY%1 z((TzZbExLw)B0ShK=$S7Dn@bv^j}!RB%YY6uQP$imC9XnU~y+qdH<_JLqkp)?wZF> zWexY9UKz~JwQIe>B!n`VgS@qCYv}&-r%nywS7F=jn`_5+R?iKN&YgZc*mO6>cGtOu z!9DZGLz3PLK1+Jv{PBpSZ>FN37aY^4ie>@@STjr0Ar0<%q zogWN+bYXDT0naZC&N*OzVer=Z%3%83uY<<7fyTF@?HXv5^bBZ}^zEQg(mBv5>HVNl z(g#4Jqz{5dN#6k)C4DDolyrW+_K;fw3-e&jskK@M^Rp)N*+)8<7kN8BKUg?7Sinkp zH-z{Cr0sQ=(psDEVVE3B~gU0;%`yN^tEdE-k z5W;n!FJVe5_dZZNU%McS7p#2b=FJOp7oaRwpw^)lv7#4;hVHo={Wqay?!^MZ{~gM} zbk1$wJU>^fJ#-UoEyu^T^V-H>6m0^lHdvGO{_qnkpBt`?)J}hH_`#7kt+qu6PC%QY zKlC_PVD(`(Pc9{_`=N8H?1wvR8*_t^PJaVxXt>iq4R~!8YxmYVeUJpjJXiFar{~Z^ zfpH;my`a6<=>&v`vK)m10Pn{jP*Cl+P=XfwsQiE<0)Mg7h?a|eK_cEGJgA}fu?NY6 zxre}-_qyW21QvL$R?JHQAnT9ipGg_s`GO&I{KHDCUP&&mW&3x*H4d1ECnR8H*nd z#SYt7J%Q7jA2iSTCnp+h&kvT*-GmH@u-wG5yscwQ%lW~1^xtvp5&`tw72%r0syL5D zqBM4)G@!$Sr26sx+M;;j^#tSBBYna*(Jv3pHBX-@&G(sA(%2Jbc%~LI{xiaPA(7(< zL%x6^-=m8YyFJ$7UFQcDYw?3$r~DD<+@ZVg8Z?0Oq4~iQl8eH%9{5@-u&sGMJvuMq zIJgM5et7=XN~H_nF#tmXJU;*GkSnw zJR>*)tO$+(tAf)3@T}kn@Dafg;2Q-;fR74J0pObiM}UtBjsR2(URncu!#SmShI+NAXn$eE`rkCr>DT7@6()!FvPt$0g&ipL394?>IsB_l=HCu&9q; zB74u+*^##lyQ0bVm+r+c^-O)jRH_wJG(bDm{!-=P@SiIisdSI;ANu4IID>w2<#=SH zd;Fl}estm!_}Tb}Ke>*B7*KH3UVV6K6~BCxk3TT>#?i5O!|Gszc-@5m; zulrv2y4Ue}_H)`wE2Xj^!m7B}$3A>3zG)S`X%9X#ZtZnT?}~zMrJ!0O&AxTOM?N<5 z=EHX=3kx{3GtZ@6PeR{oU$T z_`XT0kM_BH(!}Cxr*)gCqJ=A=-#=>nz4x8S`TbcHZ8%w}Yrc8!oezu$-vIihQvSVT z9~|}NF2Dbc()*PPedWyPd+r<=S$%w*ihSZM^yQ;LMC)hXrXoX{<9pN5_f4Gq!r?`~ z!1sPi-BdXC{=4tYu6Qe@B3~Yj@5T4sIeGjwPhR_miab9X_Qm7wyzic07kptAzOPm) zddv9xADDRVOKrEP$U6A{?03dbxM%!_Khw)Ydo+OkkI^zp)k9KhxboDsD(oFn+WVb% zM0wt;k={y2ZjJO)UgY-Jt4e1qj!#xP{$Tte6^eg3{xsw>@s*IP;%gw+#y3O$H2wnQ z-!d~*2p-$O`a$HG691C&;?v^OfFDAB5BVPko*91xcy@d?@K@ts1)dY11N^o4Jm5#; zkHXjd_+!8et2;1|#eUi@$IHzCi*{{iMg{4L;%@r%I!%=DC( z8Oe-8;)Pz2e#q4A!uwUYSMPprCyZ4s@11bZXI16cJ134)12CTPs;=f|eO0JR z(ip8iq#CLgX^e+aSEO31DwWy2_ie?hfA^uc6suu9yALW>V|osRoP1mNp~Y&}?H}n^ ztQHRF2b&cGKXQAqS~o~+whkHyW{)i~q^?tq)Xnx=SY@eV)yjU0sBCq;YHhzoRgP+` zO6|9p%2iEN8~ZIoB~(*YX1~Q%p1MJ`wcj#TQZ-ZU?6+%FzPeGhx8LfilxnWZ?YC=H zfx1a`P!)HNe_*^GNI8UZIORynF_hyeCs9tNoIyF8avtSE$|aP`KYP!Fae5WyddjVo zyD1M+9;ZA*c}b)frOcu%qAZ~-r|faxofAIm^`{&{c^Bna%1M;dDd$iwq+CY1nsPnm z7RsHJ`|kVfeV_FXQ=X)}ATkuD%%Uu&Y(?3HvM=Qj%2AXPDQ8g5$MBMI_@9PEAtN)GRemEmF%k z%Cwq;QGtz=nyZ#2lh=>GVSLLqWMZYM(y?V;yBY0E%KMj(Hdf^eI%IU{QqiyDkdCv+ zcpVpan$9t$JFPIEcl0x#Q!c6~2D1Ly^D2rW^v}1bXb7J#-evd~7a4x%4y9ijhF*`L zrS(grO>VTf4*#Tjhw`Aqn0QdfPIu`_-}E{rbM`^Z)OB)~*FcoqI{`O)oM|+u|lZFhTuuB#ZQk-{-8I zjN#w~zy5dn^|hEszQ@4$KZ*QO|4pB)TM)bI@_yRHmGWy%PR7D<_S_CXB+SQkf?@kAdLF)@FM_pOk_WU|g?6~1a!LT+P850VEjv;IoGrNkWU zTk<8P86;&1V(y%P^|kN*Y4RfXD*D*V`0#rV+k?&itfNLEkQ%!6v>LzKs>fJM_LV7# zYm=-|E1XKd&^*{okj2Xksif68eOG8y&@=+EhDkW3TNBN^p=S@)9=T?R$Ljj zMh%>=tBPXneAX|~|6bFtVS_RQD?wN1t*KA{7WBVC7+KgZ!N*>XNYhL5-PYRA8`OzC z&0dABp6GoYH8MtJyH9`AO7EMtGvgF<%JR0A%{NMnRlnbYTESnMrP#gG3#GCK@WMQH z&YRhVy%@=7_dKu{7Sxfeh4d^(v+<5HImXN?!JM)tNbRKdJN%gV4W;@~R!;+dL*{eM z`7e6BWfXIS`K=&1-+MHhm}pCAO1FJy>cv*|?c`JTJN)m~ULtp#uQQ(}o|@cM-`u>b z?M3z%gvebs2rWubQ>TDsNZAz!&TA z4lPtO;^14&+yP%3`K8t7gBGjXQyObVq>E29R%^HPSNX0%AOe@ShT=W zv2V6m>1*~omhs*SKMFI0-`oB_Yk|4`GHN7y9R4fVuR_g5$KOG#WK{sh+9z8rNX~c8 z`AqJs$SvkAx`gB~j5Y_G35)r53b8X{ed=O`bWK2RYKVx>6$E zrX}QeIc6R7u)xc-g5OFUpHc>O6q1v5v$d0GT)A!*n0SI*bs;l%0*zlUVUS;1U*;8K zh4gqv5+34;BV$yyAiq+CjGZk$|L=T?FVsiI$NPNw^%V1W&sSUfPVMJ#K|32(zJIW>o9^xZF!nZ7%8~cIlYg@uXAH{` z`62$=JcOF#NDx{^+TcT2-DEc6_ybx8TYqq7y_WUyv~0z>TFUlJj?T+!D#z;OSX4f@ z_5bB+z0*8+zm6Hvd#nuN%k-wP=J!8BnsXySmvxFNk%OJ;V3dhYxZ|j_Q8|-~T=I?roet$ExMr3i`Wd4s31g zo~e{yOK10Y{5Gq*pUfDjspGs_P`fqkDkA0hzbW7MUXnz2^^0xzzDC*AdEc9<*&6R{ z|E?AvXf#Wk`mHWr(<6B&9IVP*pMw2Sd;HMvAv@OH5jaMT=vZE@C>e*0Q`xt+d#P-5 zY#rXUwpMAZxP2kR$YiCpBdcy7eo1v}--%6cmvozV;?r#$#`QJkLGytZK~(Rt9%3wCb#&~5mi2z@ z&7ASaw&d`~H%6T~(jvd9Wi{YYUVo0gAK(9ujno@u@yrW8(^?6?GdMG4^Ui$|9{3urmMrh6%UTO z_k0Z}Sf@ z^C5j!Kr_;L9|}B@D_SP5{TPX;2H@RWbxDuUS-%IURZ3tTjO$?3`>5vrN|5J+quzC7 z8BhoJ9+cEV`U7U}3X2-EH3wJa%1ibsni8c6Y^CMg%X_?h3;*+7%xmol)UbRv9SqGD zm!qr3%7!_>nPl{4J>txgvrqPF3cnnQ5UpWW%^F^u1!l4Tf?s)ywfV(eYfa@!O}*r( zxjqKpkXs_G)r$Piwv{=~{5Z#D$FAuOKMeUbB=wLUlIsvf$~J;$Fz{(TV~peaAbiX5 ze+e4Dz8kSKafrM+BH!-4K7*Wo$!R&#`VjTPbwz2q_W=WseyjMI{5A?Ke2x9|XJ9&A zYrp0l^O`)2-SnGSqR(IFSxW3Jr8{*Ptg9c6+B=@0`mg4@to1cD$YmM-SGZicz+brh zm0Q

5nE;1Ig#tCH<38vzKkz92sO~pMZ6Ghq}lZ^XCIcSM8Yn+SKN4dtrV+lWU?e z(pln&p2N)@3V>XR_Kq)KXZv5y8p}4()Ftqno*!lSEsVL4HlNdtU{iZ+de5$sZRc;d zAM`K}Tf)N3el!2gcC^+CP}Af4|Hdu1)B)Rei3D$+g}N%H_wN#xl3V(L-q@R{0rpPV zTCxVc%ewc03Q9BnFGlR|JK0ZhY(3UJ=h8}kXK2<)>fL&THIW{9R?Y0q`{&oBPvxwe z=>b{kkIU5tus82_E(jWbtqqLC6L_}2Y>i}3NWOC<%Q|6VlH=HNd_pXwXZ%rS&I|Hf zA9DnbKQOZ8TF;W*i~)l_usfH*#wt;BF0!1oUv&Rw+LQu4_2i%k#ognF3Gje^6TDzrLV$< zt8*>(&1If&)Z$7UpG*71-4AKs1it_I>MdIhtp@JQSn(vGFbZz6Mkd~@|Q;4%K3 zKyS#rl;20V(v~Y5kMaS__%pwG&D=G1SV?O|R@c{{UFy=lI~UYct|7=lE7$chB?Pv0 z*}OX!U#_rE2S!2u|jA;qq zO12iVI(ScRlQ@rU{^KCblRU88i{llcJT}EriGtwLk^ie#+NTCz#|&|{{Cy#1Ne;GT z&~NEZYGIR~HI&w?`MS!3*(;;9xtB`X!oH+T+@m1Z0-}8oyV*v{agj1v0gRbx3$#(D zw9)Zh$M@i#Gjq)o*9GM*xj@a@<5!?T>q#%;y#Pr@*HV6yBcXB*Su|#qkzZ?jm0q${ zaw8}1OpUx!y*Y9i>5oW%N2;#g9K8mq9@4c)^^rb=)Bx!^q%5Rtq#UGNqy$nPQW7a2 zDTP#kREX3NsR*eNwiMoy{!;26Fa5a)C1;@>BrZ9sC+nPGS>NUQEg1>2W}Zf#V##w- zoh-y>F=FQS3wZ)q19M{;)F=-$bV%nu%I##{rY9~sf; zIp#Ne?{#ng#HZvp$EW#wmZkCf*TtG%@<%i7ZC}hisyC|M7|uIhp_J>&N4w%dMkUqo9 z>>)`x#*KW6#{X{EK%!i+u&aUasEydm>LIo9Uox8T627jNEtlTX^xii)ir%bzvp4QV z#T~=OdzuAHkrr3_o2hU1!P-+#FHv_Zb=_g5v&JJ$LYj&+1KctD`5LgK)A)`jB4o`& zT8Oj++&O$M$0wd2khKbF4bpm~O)8wV8=qT|a6c>YKG?FSPNSDB2&B(5&G&X`dOY$~G!qzAY&%elyf}0B5PKB~p;Ik8w z=&PVZ&u8HXB@@Ps)G4bU_$=jRF9k+f*-3m3#3$CW?50R1NTo>SNL`S6AoYRG5TxO0 z-$w%XM;e4Q6lny~C?v@{7HI;~WTa_`3(s{x(%CbSFlw{sBP~M0+L<**Ka*9Q)vRHb zyt7(nm047gRcTSLtN~elsU4hk7i@+hjm{cpwG*?ZSTsFrmP2zL^0@_Bi!HZI@@B0> zT9);^L+dQsn6<^)ZO__e(cY{B?+YEyI%e$-XT4@oP><7D=PVc0=wfylV? zU|Dv;+Fi_UoLwk5yLooI>{djbva5_XJ3zg&`(4Ei%pPL7;pP)wMrMz(T!5T5i>)jA z?D5%?tX=QysTR%1o^8>*?1dIBab+yeUS+v8+3PLZl)VGKwjyoH-fhvo?1NT&B>T8U zf#;LiXDoL<`;tM}EJQ7;=l5?;R!-7#MShFtG|efoTxm|ZMO|`w7}PK;r;p|OXE!z| zi)}ABXHd>is~wax!lF?*V@=QGOvssRxoJ5w4a%x(l%(~3&K#R#e$FC`mgcOmd0p@4 ztj<|$xS;oQHsox!c6IcA&bFML)}KRZ;~?mBhjRA#F{h#ZIY&|JLr4yxk8E!Rb;~*7 z_eWMRie2Wn15kxt>Mab2ALet)H7? zxxKk5hl(w_nA^C=L*=}~{ zuFQSj+O5moXwjD3?FI!iAj^%!++DeQt=)m#!)6uDJ(l~L;j-LHle;+g9O6BV)*PLC z(dM|42pg2hOf+yP;gHW2ChUyLJtip89G;FPuv%Hv&Y(o6gk8B3y%To5N(@X4vHsp2 z4Nr`;cEb~6EE=DfWYN^b42xzb<{6Yzlvsjt7bccx9ZsyW+BLp5u|BcMa$6G^_wr8c zPVBo19ZVdt-0{Roi_Rp@8$-Ke|?d1D3VP0pKUbDYVWX|;3m z=3BHVZ>dEq@>W~4HgAJPoAb6Als!Jrt~ps1d3&sOf8HUBj^>@P=v3ZWgK~=UE?Dkz z(sL-opk)1Ij_H|XDp_o~X33TY)j3LB?jb(Z+Lta!REyljE?SI+UE4oSvKlc2-cM%%?n>}Ze=Pc*ejl8VnMZ+bpS#9t9eijYPA7at){E-GF#^>9WA%A@SB*W#c z@Mm7awRry2{24ay?0magN8k4so|D>_Y8l8W}<~^T($ww*WBcDq}Q}qm&%1RYA%t~Pl z0yTB0#Go8J#>d)~r@9!FtW5Q&1NEuRrTV9^zQ`MBs6``EqYTPlo*HYp38`tR$%3;c zrslwUW@^4c!lf3a>`DV}h0U=#wIQ{Z+RdqLmJ86%)E>hnj;Hop?V;3BgYu&06P{0` zPFbx(Y1*?XyAI_SrY;-10GhA|Uft|_0C@DDY*Ei_> zf^!8IO^(#rf-43Uh6^(-SF87hEo@L|=X7CVVPj)gSML{;6*f2800n(s*vg+3DcAdn zGk(kgDr{F+g)*^X1SsgOWHUEs+}KUIm;)4y=U`suA1v%`W@w^QVLw}6H{Y^G7Y?*_ z8(lcWpu*vWBMnMc7LKu6&I-X<6AQ;14)wLGRBChKRKo$y@R6GphYDvKF5j&O1zw?D zxq`kdT^7?g4? zS-87!pW(846&|#9M+%P{6wJJoo72I#Dm+L-Ds%%2pUEk)~g*s z(1S|LQ(Pc#qZ~}xh4K@Wt0?cIY(iN@i6bY-)sJ!`C61|p$)y}hiK9kfMpBNa{5EB0 z%I}C&(NB;F|7QrsySXIobHqwGT2m9mQRS<1d3d#|b4^ZY)4y7DV*_rZ3l*2@-ghOpCg`6JfHYQ;ui&1U#Gi5{(x=xrbs=o8b_n_K&hjCmH1WS zzSQ<59z;Ay-vOIJWEu(M$r&E48!!rbn0FqTuaMyg(LW`_-t=xDKafll;*V<_ zUDKZ=4v`PBjv+Gj$1IUKDI5nn@G zU?dqFqeP3H5v(2~{uSk8WFD86QBO$gDUMscjCq$aZwYY;@x5%}dx@VVZ|pf+^;|M@ zsclO9l#yh(W+aN-6WnzZp8Nbm+TMV(58}Z5;iCWiNVY%}81k z-$>bl+UCTcB_2(DnwF=D#}JPp#(`OQ%O=M2SAi#s^sYCUdD?m)mh>O06}BwkLuocIpnJBUxPloP~fh&fL54YV<1`%l&9=@TmqX5yb9 zaTFNxTK3D=iNk_*G;}>=9{uEzc@$?@wQfQ_ACp1r(X_vpdH*I@{gIX}$+slNk#ATg ziBrs5Os1I30OA3}Hxb`N%=I5LS0Xt=d84smJcYEpUKn*;usTlsPtmFiv|LQf#ZpK0 zciQ}eyqrVTcoeI6o=53Xa!a8*5Z_Om`-#6v{7u2?uk<5VYHRfdnRCR4iC-jsG=y^n z>QQMQ^)<#bPZ-@ww7NuRh-AxumCRRJN)BZU#@vG1hpGJv`3!n~(wl|6cap!8JRa`} zMibvn*_U!6`H6znUm3$6Lf66aLHd7?HchC-`4QM$B;FsY!VK*oHlcQ8o}ixww7-sd zuOpW8MOtSPcO<@@_&c=u4sj`QsbF=1xC!g^x{M;Vp1E$)IQOGwvyXmGZGBqSr-vNk z7)M%`VTeD&w*3?3Z^TCZnc7y~F+|mhHq9t+pv@7=i=tH*$$ONaBhM|GdX;hhi{Ac4 z{3d<2W1OwoYd1@To@SgT{`JIzY5ynY{S$LN$6U`5uOnVZ+=jA}OeHb*XY@$o`K<4J z;+3>nNqn!t)N&Qo-zA?x*`0iM;%AAU6|CN7F5Dpv{4d$E>kP^SxA+UFy^c(Fs2jAo z%$rLFXRg33r>sY&K5goUiokz~9$pcx`W>|$DLd09#avy;RFY{(rXiW(loezubOF3o zkTKika^inb#%U8L?jTbASs3+a$%P&l<_wv)Xv4iqeINP1lgXp@pTsNZZ3S_ZI7-}( z@(X0XKs~PDs_eUyzpu_Z3S5%(tUEg0)6q;5vbk5QgAnA&QNjmKz{Pg%e=IVCpg z2${~_73p#EzYpC2{-46Af0B8d9v&pV#BvAF^XqKY*XiLqEb%+UT`619<`nT5YR3={ zqMSfx0`b%I`84rQh<`%diul_`3WGd|dGhGV>w`1axoee9^g`;3GynpkhTM#Ksp877oQT}E~LG{XW{!8@Bt*rdjSa}CE?*a&TJ!omB>(D-gtc0 zH~K^l_+$8v`6~(Ku}+C%@UN**9=6CV>ooMuz@0#nXTpJyp>k_eBL^%b*+}SF>j02Zujd3pS!G`Yv;VZc?YcB;k;uO zz2>)HUcjBsJLl{!S}#G{ySB=^lFTg5NrurvuEj0a0OemvCX5!S5C~kdak5pixp+)N z?UGf=PULzg2PXRw4M~nn4zGpABqt@ulXEC9XlI8KDmm4THI>io6&oAWmSZOh-8zdwHu(4qXJ`KR(v0G-Xhkn-{`7w4oh0+hsPN?}%~ za`N14Os+}U*_|p*+1Z_HnJSZ7z_KD$X^`m;a=lWV<*22b$yhg58oR{h)Bu}fX=<=V zYg1SYHa#Dn&p?4n(LRA zTHu$KTI`pWTIQFQhE}@L96~*ww-KyMZM0}hYP&_dQhO~rkUDJ9vD9l8olc!ID4|kz zRY_eb2pg^-vjFRZybBTqSS^Se`#B1l`(+fgDrjf4iIReh zg;|A3i;4=HT2$hDE-Wp?szSRig*`0lQ`q020vAESkitQRv#}t8!l8wn`G~^xR^h0^ zvDV9k!pYW)>!ZZw!fDpa<-(Z`ab1wDZsB~#Eplk7Ln|Cw?a*3>HaKMG2mEbw+)jfE z_xR(va8IGl3uN;G+4z8Ld_XonAR8Z$jStAi2V~;|vhe}gSW*WH_nQcS4motxp%V_B za_Fo>7aY25(dmYsLm3X$cZhw8mFslFl;esW@>jUi4O=?SU+YdctZ-bVK@EHPGpb>) zhSmEfNU&4HWnZo3y_Tk$i@O>V*#?U0NHX=R~q)Uv0Q05z@fno4Rh!&hekUz z&Y_78O>t(Bxp`Ri5sdeCsO(I!e7E;B0<&`O7%cW9kM8y(u>&~}G*8B{Q& z;a=kz=zv8t8Xk7&m_x5QblRbF4qbHUijRuI4rMyjz@dagg$_0Lky|_63Rf_rsJYdy zFKXpbJBKgyLt_jo8t<>qMdORC9gwvHvatZ!Sb%ISKsFX2 z8w-$)1<1w%WMcvH%bjG(FU~2NS~R<8hI~uyDw=1bT3EEiqUA-aELu~vsc606qOCA2r(9XpiwyG_}!w>-mu1_Cc=(HE(pZ(FtpJw9zSt&RXQ!FUS$} zWzZki8j&`-(CD)H7Suej3;H}Uzu2>OZq%i<+W$YeU~K$nkQ-q||6fLFbJbs8(&z7g zH&=5#2-dKF7p2V}xBk^wk^HqTSoQwfQLtM4e;E~L6xTOv7_MI6ap3>G3Z?r0-m6f4 zAV=WUnDK)NYxcb_XasYf(N)Y_3`vLTxu;JaewL8yTK`_RKl=*<^4A10FW>`j*YYsDF}a zD-vgFz-XL-5GmOEGMSgiyhOY~q~cX5I+s^OM5r~_FbpO(SLO`m*_IE{9%nI-%Uri` z18vN;3T(EA=4)2EvR>rL#PO|-d%+FOM2ZXtv7K)`*;@VYFo zicA$5UeBhVW^5tK`IL`Seodr$OYC(Uk?Jk+sb`YeN;#9vS1BJN!~%+k_7#sKwbS*l-2#29W2>^16_Jnvz$Gg?Po6$9e62 zO)T{zl;0K`a~+PydFpkh=Z`S&M`+`pNt9fvIcYry1ut2x8iO0E`AZ^)=NSC8n%jMH3)!)rV=uSHSYX|tX7=9-*l z^wWq8R~TG1A)aIO5EHEaAsFYW<*J@$rW7*2C;mNooW+AS{kobGG9~nOqgZNQrJznR z*D3Nm=WDNi`74>x*h!v2znGZ=&S@;sLbm5NeFF-k{t~`F+YiQ~pMzE|qifIuRNLSvc0dm}}Cm zXWKSm+csg_-oSWPGuK^|ohchr4yNou`3!wNLZ9ED&qpY~LZ5rtR^Om}n`7n=9B<~j znyas<@vqCmIc{B#_RA@GEsZ+LyytlB!PQs7@EVx(YfCh*aL{3T2s78c#O7KWvrX^8 z>lR+4=htXs_Cv0^mIiYKQPpuR&3WcE*Laxg`bN|8@8o$!4SK%_c`s6)B!7}+DN0@i zr1ogo*lUe?5%<;~MXtRfb-BzVb3K^)B|ZF-rMyiK32OgF{yg~$as`pPz!KB1t|_4Q z94*b&cYsO{-tY-Lv&w(Uz)h!=S27oA0n%cmWlG(2)_&reh?^u`#`l#-&m-Y!5GB~d zDe>^R1)sP^qGUVLE~LFk2UMu!HGCdMI))_pH0-eN<@_Kf=DUc`9O$p$Go`#1VWdpp zVx(r^7a$MfYtaBH0j{MAwP+4p2%Y3DL&E4NsRY*;++tu{e^N3XpV-b*KLEPsz=LhV zo`uw@WEl8XD%@fdFv@Dt4xe}7vlCJkQg5VwNVwLb#So<7NF!l`elHo9_I)Do7^Lw? zxF(~;RHPY5l6N-JJfww4OAyyACa=YEd|!pM25CJ~EBKrej+c~{l$Uhrnd+G$>fuly zhx!u@f?aJ+@(y)&BODs#(Abg**7M|&X+YwoHYZ+YQY&aq$$Xn*k@FIiyVP+jh{RtI z{c5LO>(B;=HWO`w_nnU0<51vbzvB)WgdBnPD7h2hPC0bep$kNpd!|}=4rK^xQQzlU z<~&}pJ` zEiT&D5Ojqx1GNl0lL!j_GDrdl=!s>M;;O0-1nT6VIi%J-L6MsMN7 zOIjKIoZUc&hB!3bM0!YqTN9I9Cy&6BMu#Jc@j1?^PZvhJorn3ZdOQ9qIy7CL`k5gNJTf7K$atw z-Ygtu1vo)HZterrAIR7Z0vdrd^yX2JW9elATHPa>3~n0H%$w)jJfCP0&{D^(aA>td zYaQBf^JaLEyxSZX*cpGK-AU~pSB7x=U5-Nz9d+o0L&nP~lyjEcg_|$8GSS0Js|?{< z)kn&4DAlUC2Gh%pt>7fU6|e3ur*A!L9nX8U}P1k#M60$r{mWVyh{w#R3ed_{&$n9EY8}uwsp9sm)4m*Q>`1k6W2NcZJ}su+O=*hT#Jg<&0Du>-45DL zNL7yOEnJ`_>gTwD1|df)mt#ol;Wo#})?=U<56`tYdYL3zqN%NCSTx&t4$7G4xP=Za z5k$|7nO>GV?J9>_wO(UA_ZDPx_}p@*UFFbvhfHgr2U~A-b=%=Q?{;XPLkArSqM}{v zBk*z(>9}l#TAwlgfX+M5>s{VUJ}Om$N~1{iXos;;n&nW^p&~)0O_53*N4t`3{&u>w z6k4n~7(a-hw2MPM9O~mxpzSYQ>7W`phLRfrZdA=2rDMTOpmwsqUX=osPOF2PNp4OJ zyZNPyU|losQrfL>cB|2!&y!mV+s#NDN>R(wJxDuCh1*Xq&>q5fV;681K_loWbDVH? zr-06~j0-+jg7!1}FQu1zrrLNlBWRP+rhc28HfSrNVnHR_+B7TK*`}rC%AB^sp~^PB zthTS?0=vN90H+PO!Q_V3DEBUBH`<|`Hsf4Wf=mvdyUS@uJ2cUuptLE@-*ktl<#=u- zt-+ela@vUw%@tJY+O*9A*e+&{Wk4&5(4%eEIkeHCEp4_#BU-Gf7VRZEfNzHdwKxaw zS>%p^1e9*5brV+al2tHEmmBQK`#Y?obzpdI(}vwr=p! z$7%aJgtg838!AZl>f4T}iCFF^-!5p4wqt#+?F2z>CnIsKV~&}IYdgnqZRbO~h@7CM z4y_)D742HeJa1RY9EhM@UqOMk-2mS%jT;PZ zn6tY}ytLiy+qD}FG>-lz0!mM8a($+77f! zP+6kg-gXBh2hm}Njyd!i{hh91cg|@qw!6|Z)jkYuEl&Kk&lD|DgZ2rF3jMt88#~lo z5WToq+Opr6x0SPN=TIjfndsYBA%Aajv7e~>wV+AsH!|gv=1D; zl%Y-99}$k|IJGCiopBs$Wwn=lRIYp!WX5R+;VQ^g z`n}o#s6($hxW42DKs#8F^ks*;I*jfxjA)#o4imvmq1|+#xg8dCm?gjC%ZoZJ>##V; z(P3qcy!6t33ApE-=XLbA(YNcc1!z0DT|j$T#sQ$i)E<$hc^=QalN^V5t=6H6sqiwx_mY-(HuKJ_SXi;7VmWLCt#Zh6;MX|2 z^-jCVp{)+>sMrnbeehD76VC^!6?CNHxGn9Z%X`M5^A26|QAb52o?WCJqfT4TM-@vt zX8H9XVh*3{7)Wx!_uR46p>l`12I+!)wRY4y_d=d#@cgIBv5;f!#K8(3rd(cVg6X40YTCZogXd<0pD4%A+X<)>bdWWg_C%+1XbC~51a-RD=}PBt=S;(OZs4?M9lGqJ&IyMKeH8fX+_(;|xp19ZrI*{e zop0Bf}&wAEiam&-d4PppT5UVxB*1rRJT7_}b3B9U3mE zGiF8SG16+Cu_AY#M1*y>^9(^PSD`=7kedw-J|P7ybZCh~%N;^4*sMVW@D8*dX_Fw? z2ADm|c5bwH-s;K#>b!$F1nqWcAJM@Yc1Oq^hn*6>;J!;wYaoYYsgGAeL6wnBypi|&vhN`&^RBZLQ+xIgWXQ zaa9v_eGT?QyPk#|At=yxZsl{bKiO5%xvp1yF0c!tuMGE0Rc4A8q6UImPOVH-7D_Bc zjU8(45X!OIz^cF_z`R$vCLQ=)pEc@~&}cy+iaD%=5}km0K%!h_-S! z(vHf5mHX=AsI3JRB-FkHE)2%=K)kK3FH`JjK4#7J-NRF`%O>ijiH<{eD8f65Y zXSy77h~~p?k>i#+M1S&IvfB#Bp*68jxEWEbb!Y>%!fg{Izj3RaIZOmQ#UJRmD{~ zvaPLZCZ!Rz6jW7)R6$e;)XO1^L42>t1=<1B4z7_S&<>;at{U3f{zgMPu2vZVHxb$? zPCH#t$>pk9RdcHrShUz7%OS^HXSd90S32~(L+h$GV#L;mzgis2*dkh@?Nz%h+UxQj zaOki@#~gZ15Ixrw>1k&-x9Xg$2awHCgJT32o!u3O=(&5iXR3RqU*GNxe9zqz4i!4o z*hfM889Q0|yEivpg7yPi?0c@d;^*z&%At0GWWDO%N$k2;A@wHe2Q<*3Aw*bTyN`4n zwXzO%A43jnsjL=Q`}dn|g+bjX`J8NVuF_&m!^4d99Clv?Ub@e&nWOtW*iB{*pzaHO zl*TO~x4eeks+x8kFcwUXV5OGd72Ve(t*M1J)#SQwz1pt(4sg3!hV#<=BDjO(j`%s` zNNV@vON={4#hc4vjFoDForq00_AuBYd7Ju?LLtncTLU-Ugu z1MGQ-B>EqFzZdUdu=g;Hkt@gl_X))6eLsO%HoSLNRv z?jShyGNn9&wowRARnw1#vLK5>_d?F|UWa@kd>v%N&|b)|3xg-m9S3d_!qeH{VGd-o z(61mHv8)!M-;>Ant+;~*@o4j;Rde^x^`Y%x8i(;TIOLrPiEDHr5Az<8BlK{Dcb1fs z=iO<#HA|^r!a-o_+KU_R$S&nVsyZ zV)oR0_S9weR8vOMnBH2kcbbNzcbc-DBdi7QHmh+Low)|T9;0o_p7O-fJmnWxm&=&( zM5}Q1G_bk<>_(Pl?%^{}Yrco&`cKM~p1Oy3q}@Xgce6$>vqtwYues~X+@tmvmiQOO z(4D1pCmuwce&68`GDFzH=I*`0^goFHchLV}w$gL7e2$jS(HpKBM^xs?(>D|U*+|yz zF~RCrl#kJV8~yNpINZPWD75Ao(vM4t>M@oWk`mQ@(*Dq5B&f$F7qnvkwDbaa^a3RA z0JGQib9AYtEbAcUQpWJOw14{Doa$MYlFfSYZY^!@s_e^Jx0k%SJ)4SkI&ilcps!%#6Fr!nE$Uuj$9!A32P3Z5Zd;@Ng708YWVi`%Zo@cVVj680W{m zZP0!qw2U@#N64_y0mz-K%TC(xZdZK+%e{e-OrmxYwI5-*=3c!PWNxPA&8&5VwcxXg z_05c`ih0Y$Qtg+#YCm&bLq5U03Fd9cH8Y3pHk0j^VCmPg#9L^Oo6`_&`hB;d>MLYE zA=3LEWW+?mUXIX1gdP&6M3x?rwb4Cq6|w2ts9i(JyB>gdS+|F!M|v@aUbHMA z=6#m>DdMNdKTV%cN$K9TEd6Ol^%VK<)AIYYtWRw2Rm~R-aEuk${?^gAP;p#2l%BedkYYM(r9o~j<>9b+-xe-`tO zqJ?9uMRw@Nfo@P|+p81wY&cnD9G1+E8K zpElRAtm{Jkh4=6rR&(DZ@G)BAP9dEc+6vi#kvFiBABN|y9-g4ApCNCa)%{(1`!4h1 zULj~ZvXwfA@T6ID&z9zWkZP@34rVTG=CZYUr?u`udviaixf`zu_W_!7t~CiseGBp5 zvyhCWiPS~;_m;_hneEwD=K1SaKX1Dk{d136|A4rh^X_xZ^*J)F8Es=SjinTCwP3Fs zwJjL4!R4Hl=4tD{Fj|ozZZSho?ff`Me`GpS|3KJ)6&-&1b)4Fdp-)dOi(Z{Y@DA z#C_hqs;?u{mM!2(oQlspR!=kLe$?`*@c6d`w2#i?9h|I}dR-z=OeGy9! z(@&J`Y@S5^EE#kEF8?vW-rHxM!5?LtMA;@$wnmiw{yHOhnGvoh{u%x7+3M<5mc@Gk zbwkF9d)yFhQ$B^gDJ^kV8JP5^={F1gOl(3sAQy&Y{G0!LxPkURq5V%-;@1VM7b*Rx z^QHd5(+Tl@&);`7G+;{kd^s;(F`b9m;e)$&Lc{OYFsPvIyDdx^>b%6etQTuha zGxt&y{|!LRm3je7S@T%dO7W&v(S9lMQ?z-SOf}cE|S?Ga|%9XKDN}GfH$y?(lFpiOoV{5deeL3w@!h64^JVd^Pcl0);AN~%iaW}lVBbon=fM0@gmv%Fj(u||G zm~(kP=Q8iG*E=|ui#eC)b1q-uTsHqd(46abGnRWZZOnbor80^)DdL&se`=MhN}4V4JjHJn8qr zf1ld#)AJe1v$VX%Vi~2oq+V*a_*W+>AENdx$*Yb~@*g(TS&3hLoy=3Te3J4q?H3dC z-xcf=1>Taf)LWbjZ^_CDoTQX(#NX)WuhsaCulVc%b&4J~$ZDV-lmEm}KlNnGGGBN+ z0YcWj3-oiEaP`lcZjH61^Gx1|xZcd@1qUl%-_ersRF<>TQk|^DKno(8KU^1IJ7g zdNX&o-^|f+19RDX+&MzbJ?>4o)-{napf0kG{5KDs&l34hQwsMWLwkiD?j?UO@n_h} zpW)c%ziQ|Q$c&@SC?myEnSWNf$ey~$-ZB5AF@bS&Cse2RhuT709STA6pGNdGA?ziB8AB;;_0Q0?i03n&9Fce`bc77FU@$h+ zD|`}z`KJXP!k9$f5@@61LB~T6kZCBA|Eo|Px(mFLBV+gv5X?&Zk3HsJ8Xo7rb3D%f zWZ*Ll%yTvLFxHy?yY7IYC+{5V@k{Q%dVo-0(ywLg7HsP8)lCAww zwoDTqBjR3ni2vIV>PE}uEUV^!jG&dUmn)^zr~eGL$z--keQ9mIMp{N^u-22=GOzLZ z4d(wz>a$If(i-r9*1%Jdt{_7GM}&R)LSOdXQT7*~h@k7SefZB6=HDvRQHjC(m{@vW zqx_iId%FaOTFa>L7O{5bpD4PqeY#06Yg4;!jK3S>X(+j{KZ90MhXkub%zHg$`hTI6 zvE9nZcVatq5K+$A-X|#Up!NY;ep9eIOvXIZVgy_AS$ck!eA-hkbT4XqF^10c--|8WnKf!-q~r=I z!6A2c2=}+apb&hx4hEh z>Ho3wI<>EJOL{^|!IQeAzs$d~ylL^r!2FG+Yeu5UpP~I3@?Rz2TCivSd8Q2`Z$qBH zD6vN*b#KGIXv4l}!@h_!{xYRI;1^HSERv$U|+oq1cco!fAPw_)41;TqV6 z?Rlfv;Ha;Zav#g({{`v$8Oi-D>wcCpj+W-BC+ArUJ{bkiK9Lr?$a%r%plFYC>LO>5 z{~u6N$_36NKIKK7XRh;X$@83#$0Ty>&xobGbk?e@Gr;xKA|+=6_94kp(<3&?-nHy? zNsCH6@?G#H=@6tSQau3VlYey6NhEBaH1>0K2@>`~$d%(0TQy0NSNzM_2Fa`YAoWKY zgoLvWdITalf|5?6r1R>MMk%_U&eBPEK7wCkCAw6X>n^&7?xXwbL3${1oYbRq5uU_0 z0q@Crn!NwVo}U|Ajg`9k_G+x-)kCWB-!;`k)#H$r)fi{hpON`c{UBwfjQ;8y)gzES zs;eORYpuHfmEFLfu5JN2?aI@Td9t0W9!7ajbq?^TD?f#tLhXbr*iWtg&Xv88&r#mM z(z{mo0rTA}`_P$-B`VdAc&?OwUEy_$I5IA&&ghlVNaK(uB2BSrIzDG1VHRm@B{cT2 z^)jTDNY5j!L)wV61!+4HwqRQF>_s|&bQtLv(rZZK`!v!yBTrI8|97lCU}#*X?V}Xdk)_7@m_@YQoMy*LrKw7yo;{b`IC3_jA5> ztn9T{=Y_BrUfte11$n#ta<0BzR>0~?*_*E(FGp{y`-fyTsFY*4)m`O?aCHwd-;(3G z)st!SkQ@uH<~fGy339Z$x=M}$BNu)XR^Jw?g6ttj%Bok&Z-eTGhsm!F zdx{Tqx#38p^(Evy=7+c*Tq+O1gD?y>IErX-6^H3Z_74YEszq`i=iQqi@9oV z;@;IxuH8Gi?!UmNQQgUP{M%gj7sxh7y(smqKeGN<;Cd>dJf#%YVwF@Om7`Ls7?GI& z3m)&u+VWjF8eYwFPLN>gt*GCLdCfKevw;7Ny9A$-mLsh~T7$G6X}QIl@VOOf2hwgN z$+r*bAQIMhZYxgW^Ni)rxTgCpp)>o91 ztB2^}c#p(;j2^Ei>8W}~+IzO1rx)racrTY8Mf3Soeq=jE>R2hK0`f z1au=+SQRLx8mjJCCwi(oR9E#$b+;O%M&bP_^##0#YvITK!SYRe#bE^|X%Zj%vH^jPm}WKdMKgn9pKlB=r=$i}YfQg+|zx?$C|( zEBaO4ME?Qr5-;1!(=EJ|SFB6D#@>zi7u=h?65ZANjQ1Jc&70^=)K%UOy&vlC-d^uF zIM?&CcS!dRJr;UQ_Y1cQx6-$V+l0$?|8PaPLJtae3U|>T4|fmu&_lw#!oBp+aPM$$ z{mF2jaDSXH9u)q#zB~Mh@F(>>;Su2xdQ5m^c#QsRcx-r_o)8`%9Jt;gj zJWYQwJUu*JPYcfue^oyeel)yDe>wa__-Xys@H63O^!)I%;g$Lu;pf8N(T|0{8(ymy zhu4LFpqGRRwOHurB_9gk$nAJ zq%_h-e>>7P(oTOTQXVPS-;H#OROz*mTO+sXb&=a5x9RUkUWpvh>mx@aNA;%2vB+`# zycA>Yvgp~wB8>1d*rNsA@XMAynZqAR^+1oMdagbJnPxRL4 zC-gs}pNmfLqS1$<4|y5U8POSDJo-rVt6pYwUUZ&!ZFE=ks8>Jw&sc_cL+nqnKY8sl z24#HQE6;c&V~$smu_R-O*CpebjAy*A@k#MXUS)h&e3#cPz9+uNtIBMd+0yHt*&(xo z*8^j#qj%K9s^gvT{;WdYNgTV5c&9>IMMI%b6IB$tA=FuQ3w6cTa&V|OwvfX^eL{WI zr$ggHQ`CsiRBRjX3(dz?dVFYM=y~-(XbsLM&I!GQ-@;FYj)sn^?}v_s3)K3^?U8}% zOk_}Gh&mtnRAjjNXJkZVgnB!2SL7~rIWjUbO8qM`CNf5=$oR+_4SeGBkOeI$oj|zeN*Jekss@pk)K4i z=$j)uBRh5L$gap9T^iXJ`HgNLc{%d3?hv~^)=XE#n#Y>!%2-LPrS2AM6>F_~#M;E# z>t3-Au@3sfv97Ty-8a@F)>HS7-4eS+4~X@N-L5|x`$%kn{zPnG?Bn{5*wENe{i)ce zVpv^cBVyC_h}i1b&-Irx+h(@aPh##SGkRv+lF>V3V8+KXK9TXcj0qVJWK7KXe8!}V zFJwHJ@x_dXGQOPgaK=|MUdZ@e#_^2P@fXn>_v+sKg*5Mg1%Mx9}NtCHzMCoL1p~g#W3%@Y@lk zW07zqrmu~}BXNCQq<-W&ofSz%igY5(9sTh~1$l#fHU(=`X}S9s9JN9RGa$^ZLO|+z79yWOmK0)L+C1&50e2{V{et zb|UuY*lV%ZV}H*m%xIR;I-@kBO-5Np+l+P@?K8?VI%HJ9HXd#lZXYfW_r>b_C9JE@ zMs5Y0i|tkGaEEYJctChy_|xIL!k-O)E<7RpVEEzi%-0-~c#_*3KACCMD)|nZ- zGCq?`cj{GpP zG4i9x-pH>b2Vxbmjk<>;4H zC^|Qaf9j1s7yX%v#16*}t0Ls7pYh?0z8U>8KAJH&V_L@aj2Rg-GiGIcE#vF)FT{7k zo0oY_W{y&siOhtOeN!DP7dCb>hJEnZ+1MK@2G)z;7XPG_9e*&6t19DPjz10jOnjwk z7GD)#qe|jyS!y1ppm!b7R}EfU6lg~EQt-_>9o`MZGgZWnQI^bH-sp6xGnyI8%*c$Ri=QyyW^y)vaL_J;oJ&cp}UKwn(nHG?xGV-MjE@xVgecv6%i2uF##&UfT)O=7|f13 z4l1LJIj6xmjA_&{N6d%{ii+r%Mg7)URr;Dy-M-3_7+hw35?VMAKe$v zoq|VH><6yulB*`kRm;d#eQ?#Yg$KTR4(@czSbZ?o<-m){}FpeNO=HrU0iq)A85+o&B-@K<6Ml>tN?#yy8q}CjL6hnT>OTr{nx{ow?XQ z&p8rTvd~$GIS>1P>pe&#Nx(VfQunHIcIMfd~*EtQDTS&Rr)A zqJ($w*LU4_v44ZRL4@4*-1kJ(ecyc_&)w*56s0zL5GC$r7b~|TdiXnz{LK9f`@eL* z1b*dyC0s-j-(vq3cMI(NckXw>^)SSQ=OO(oNUm+O$<@;{y4@)#9?s&;$BD0#={Zw#SyRtHaZT(Z^Y?hN7|P@P{vkZ z&n>9Ia-f1)`|=i4Fo)KpFRe+A)}$}(h(kMKXgdsTM?l-*&~^m09bws3b``y7K_avr z{b)OiXggxG9YwSqlfd&wh&H(+*nU>-EO*BCu5u)BlpKXUqh%YmZ4}Ur_GKq|kUR+c zZA8#UTeB15g2P1vGAQ%F4-Sm7;&^Q;%H;U(Z+~lEG=e^amQG7q&gDM zwb92|@RQ?!$E)M9XAz>0Iz}IDj6Ukr1?omzn~g!T>SnAX)4)ii8!c}Gd)%Wc=gq4HQwD{5TSHekEzE*55_6oVWZcGJs7Q&sn^x(VhrNxH^oS`7E$%?>Miva zw%<|ji1F%O#MUDjTW_b8R$>fTlM&nL0IKxPIsyfJq)zHI_Ln2_9*M|%0C0P~J@yQQ z$~;mJ)`PJ<9I^OFy(6OVk$PujlE>(AdK_>!WS&RrL-Z^$8qxVeY#)p0yuUtPA1}u1 zMfyzav61>1`Vt56`pwwB6?MmB^j-R1{PjMtw=sIPegxZ(>Zipx{fvGF+t2D}vHv;5 z^v(Kp#PlQeTD=zAZ|N;K^LP4tvAh03{~)$=V3@=J7#UBDbbQAbEsk;GqSZ+{#lR9M z0ejMiDMSt3;R$*=Jw-3X58J_?*;p@2@70gqE9MMyhT$0Nz5395IrLi&{g$EM3ej&l z^jrO$Hm41KVt05i1Mjsbo@>2VUwSWx-mAAW*_kYMM5MAWj@i%IPYgoLG8Nl1oEf;1 z1Dyj!KYFqlBAG+rEv!Ft=+DB8Lmfmnb8w9HYBA?Xcr}B_bRmwhUd_;}7168pb53v; z<9e)b>xWoqDX!;q2U!j04Cf5Iqcfee@QT*YIrMY==;vbabHB%)Fq+!3J2OO z{aiozwrg;n>zwOwjP-EFxe>li)2j`lS91_?-GM#UpY4Qr`EGow_aJI+b5=X6;RXNV zJb*n9IuDBNore%d_i!FY9Np$T?mRA1h{B!_`#4WJPhyXath3J3&ePa_)_E4&&pFSD zdgpoP1<~Dk(RmSjUP9cxy|czyBZ?8fy^bT_Knz~*yy?7&?X}KYobxRQ*27untP^9M zx1G1~taqGuLxdE~K=Qz(7&X=N_^Of@zwyn2rbG~)H#gSW_EqGtwAqHwg3?xK7h@Zx`e!9dh#dgX~;jbOs4q_kr{+*x|bi#Qm z+zK3D=~jv!Zq}_54NwoNvA@Qx5gpu|TPwP|b#5J=TkkgDJl))GIKI2v1IP4sd*l3l zz#iJH!hk)NL)2TH0o(oEX8d)4I{<&(-rZgdc3a#Q?1%Con%sfzKYZpo zbbc6)Iov%Qf1Tsb!S-BtF3vX3ormrD?tC%MJ;FT#XE@3|8haMF3&d!|^T*(LsD0RT zoO>L$k9Ut3hqx!WCt%NFcQLN^ME68&FL9UPRZenG!uBcdDPk6M!BcUz)7+(EclS5$ zZ?L`0Jsp2N!#x8>p5>l{?Q`98anAGH^Raz_dm)bbo%=iNxyb#!Xmyvn%W)(W1pM`K zcO~$T?jLc@SGZSV`)c=U(H|<~wYY}s-0Q^7?)C1CIK$2E&G_pr?ycCq-Tf0jhdbT7 zvF9H59-Qrd_kNt=0rvsydC+|j$2{adgjf8l`v~?w>OPA7e{=ta{g1nkivjKv?i1Ma zr28cP`jq=DwxL$ydwJe{9(!JJU%;Lh-514&+DnP|W6uHgCQ+UsQTWct>Cx>KUGEb(RrUL&I|ycnh$9p?55{p`?j=a?l2= zr(uuPVkVQ1Hdt*9dzN@7i@nHA8=$kDjy-31XW|Ud+3?)6y|b}>u6Hi>|JFMnc!75T z_CR^VyMXeBJ%8~20KCk*OjJOByBynBdVd94j@#zF>8;0=Z}2wZRX+B}KH zvQV0<#CCpXAKu)r_N%d7JP#(gMF|C zs9!^{f2cncXCCJ7fWPkO?+D!4-x+`1)gK8Q<&VOiHopzWjPb`{d#pcJIMBJlEBxL3 z-ElVPig-uRys>9*e{V6`s*BhI1sumu_9x?-r}#(V3`hGIHPFH_M*N$6j1m79|1UVh z{m`2Zp*l9%f7O2#&wayxL+s$c>Axwe{I&jCF%)X#TVe%{~plp90_`q+D7hX20*zL@53^f!wA{SW*P#B~2d|3k5#|B?TZnBso|^=vAX%>JUY zX*NSe7ijUrL>&6#aFKv2zk}!mWqyQ6LZjPR41-R$ix>{AZX^&2rI-lSZY;LPnQ@|q z(Mt|0-X1t|Ph{kFgMzuYXf#vI6tRn$YNm=^&2%#z+cV4zY#(S26xC+7nGHPL9EsOD z+8hl$#vB7Y&KxJoq5dB)_Ju0A2-_!^6R^G5EXMYU=0t2SF-x$0k~s<6C!3RTB{p`- zK|TBpwof;w<4P_x7m6&SslJS+`XZC?0M5V0tPyR14jFDK5kywtASU;Q15G{7+PpxXrRanB~Av@ zMHH*v1mAVZcN5Ga`OG2}k=HulwMD3|jOmzAI<8Sar4wMficD9N=>}lBIZ>*+g6}%y zyBhhXRuLiVb;x=HJwzjmuZQVj*l!uI%WRa-Y?KeaI|mu7xyVWR$Vn{(TJCGecLQdo z0%WFEi9p{8zUzSR-XlW#Ua;O6Snqwh12IRX%%wa{zcZS<_-z|lg9W9jG=7O8LW8iU%=xZH!Pj^pP-U-NvNoK@6dR#+a z8z3V#9U6f3wVJ-Ri2k+MX2+lk%!N0N(wm0qO|^TZdnDq1>p=r|k-G?gwZ2osU!Da1 zak6_dUd4LKfSxizPgw#_xeV8HhI=OddKP@72k&?Rj=2y%QqxBk(MOihM;5y);T=7A z!Rz4_Zh#jI+?(8+@LXiyu#M~+&UQDvU=h7wvCYBZ$VcGwis|z__c8Y|?6ICNpyx{> zH}^EI_8IpXykG0{JXp-mP{X^xZWhsEI<%Ojw3PvJcJ(;MT1kV<9ab=-l?;%->nTEh zFTb~l&{h`FR+i987Skq{&?W}3gTwGF1aHf!$!+Pe_#T?t1{u~1S|OX$d0KhF3*&d9zqWBNFw_Bf;V zIHUGBqxLxC^*Cen&Wz6EjLzeXuw#s&jGN<(o8yd_I~P<$8x6-90mm8X#u?*w zW^~(`(QP#&+OCXf>lo2?WfYs>c0!5B2b7&O7?Gr@7#lOr&}5m>>oSHZED<=E@OSQAU- zA=a$n*lXh0tL50M=h&-dG#Phn^b=>?<1+4vGvcXZtP?mkzKJuki8HdPWMtEokxiU2 zjb=1c$v7rp>=I|}5@!4nXZ+Eb@kg97M;)V$O2!#+#u=52GdeTE(2Obq#uJ(`M4Zt> z9pi>Lyx1MX(WrU zaF2D5MOHYp@-b7+^eu{8G2tb^ae8Y?yhC%3EHN5GW3Ld8!S{Kd3wfO<*vfBEKhGD zPY;oyC&Sb|%C1$#CQ3uradN&g89eGS)a5 zYbCjAocuISP8ug0tt1bPlZVF1J>%q_ak9=hS!bLaGfs9HC%bGQuWX=YOpre|&@%R= zWz5ksCTJ7OX%j1G6KlyIQ?!gt&40Pl4N!fGP_P>c4_jsVsf|M$a!uSy~xxgnVKR~DEbyqOAkGd*Mr74T+i-#zFjTc~|E zQ~T~g?Ryxt?;g~?o9IPb=tW!TMO)}aTc~~4)0ehT`|d;SyN;@N9sOzx{b~zU?>eg9 z!>D=(RK1(2dIwa!hf(zosCw5^^{%Jtoug0gPu07bs`oIe-p$mzo9U}t=&M_(cQ;e- zZl>REq2F$y-rY>4yO~ONGnMXUD&5Ugx(8F^9!!n9g&Ox@YTSeA;ajL~H&fRhMsJVR z&!KA%qoO^GDt0qf>>Pc51HF9%_3JeC>pt}St@QkHdVar938tV{x67rIL=WFe58p};-%8)!T42{}p(7X3tHaPiF$F0O0h$wxDz$v zCaS}YREHhv!VYy|NnJQaMOaf2ZlWUWQW0*VBAlWY>`)7Cq898>3+_NII7JmWMHRRc z_1|vPe9kPiZ6@ET zCEsbG&YPpc+e}rrnXIRUs%|s6PYYR316fZC_1qk}Pgm->%~W!mso^$L!yQZ>)I#-k z7}eW=>TNyM+h*#v&D3q1soUnL+cs0T9Y)^NLdMiW#kQG>ZH`)PGgaDVvZod*vpMpn zu2g1oRA!s0$u^Tib)_2HgG{Q0YHW@=>@f1F7BZ;@GO4aqV4JDH=E$mAsJ%9mRke^$ zwNP(uCZB4d#@bAEwV8aXmbz*)b=79-sty%ZMMbp-8J0(eWyq~Ml2x^mRkao(IIEV% z$f|mhRVB%)e6p${vZ{{cQ>|1*wa-}4>Qb%&7R&u2xa;1)BNv&i_ z39_V)jN=ShQb($mjZ`fqRZB_LvVy8*6IDw|)v_D)$|fq64wcFdR4ThusZ3F+)Kn@Z zmC7b6l^v*5cA!$(M5R(vsq8?dvWZG%6P3ztR4SXOR7xt9O;jp7kjJ%5YQ3wd7)^~YxFk4@x&E#!bL)F11~1Y5`iTc|%aQ-2&rR@g%2aTvK_3%Q|3Zs?I4 zwvroqWQE0Kg{@n~$W~7blMg1z2P4!I2eylvTd5=tBscWP4O__#TgeSQDuzvDh^>W4 z*(!!DWQeU)47VdsY#~GR$PioekusIRf%&>Qvc(oEgCocpGgJnvsSI`|f9y;CIE4H$ zOJ%SZmB9g226rT*97JWXhRR?Uvdez{Ce+mp^*{DMhT3SEW^by5+ml^}$Sy-iB&a>EdHw-8xj3-z^TYG)y8WGP1aNwT~avb+|uycRI5x}E zzy?*oJ+fIKEd9up#i36a5s$Y2bz z^ovD=^}-R<3opg?Z!o7`sb$FCN62r&$ljlW?Q>B<93smx)HI^hG@{frV&po8TqjPh zlOWegfaSalHRBb`xA4@f3KbvfHT4>Dyf*VJ>B|$WGB)(42Hx}@R0rLQ+-X2>D(Ox0 z8bT4Pk6n6GgX-gFkcqX~(SY7GL2qj4P2;Re4nsqD3$;n>oOP%*viVN~54s*%TwAAX zP^bJ6{)#$fC=xceX{aqk=_wP=*Ur~Cv(0Vhb%rA6J7hIocuEKPJ)6%As57vlS;SG% z9D^zscjHj&Y*sT4ADPCUa^x)yJ!M{bD1xV~!5*8dOxUVtoTt%k1ln9>oZiu(>bV)) zHXG?O8!4$a#83e}M1j{viY2V*3-~p8e18S{WezO5N^PZ$%b3Guv{B3PS;{!0lq0j2BeRro zMv8Gpig88}M`k@Ej9Nw&E~5&Uk%Y@g!YzzITaDk9I$$}+TUW*nE=OHgj=HWKbuMdT zvaE{9GJ2@xxI%waL=Uxv(PirqYuk;nZX8`j99=Q0hjEUv7~_dvj3+W2S1$Q!6-Sgy zX6kb+xg1MH)Z^mR;|yyL@~WImX6kZ8HF8A7IHFvRs3P*yp48?Vsm&RVtFC0GE=O00 z{Ir(*w3Pfb#kvAJ#y-KQwp9eR9BFR5k>+xY6;aEJQ_E{48+AFx8rzMrA~MsS99>1! z^$a;_Pu2+pWTsVQrY^@@5tY6;`DrcrsY|^t#IYA6OYO-~S44j5QuQ+&fi6|QMzU0w zs-NLF43n$69F5(m{pH7@Pwg+C_iyBAtRly8+l|H?`HstMtVgC(%aK`M7@0QfTF3F} zlHIr*pN-@=wH&1`IZh2ZPAx~QOO8`Zj^lFNHj>@AWH&BHtV>4YlF_8dXk0QHmt3Zn zT*f7rspS}UIeJ}khkR{)4LO2KcF=+Bpn~k66WM_xFNlyAG?5o{XCANzb)^J#B}rY$ zVeYSl{=Xyre@Ev2I+F!-q$ZQ5##GMSUkQ0Z19?FubAL(Z{)(CVOEULYOa-cp3RD?2 z9ETc?Lk-8F0+m;Cs;NV1>N(X^qcl~WYHCuN+DdoE)ShbAKgdA}*C5hsuvb z<;S7&<4~XKLK_#NK2=V((vgag!%SZZRjP7k`pU^%TrwAz%*BN@43UEc{A3M3SxMgI z^YeB2$ttob53@!(35}T}IiStCI(YsURGnf}ol;btVpN?ZD>qYAol?T8P7$h3DXLB> zs!pZUn^M%9Qq-GLw9NrkrxaBuMb$}BbxKinicocmNn5||P<3*tI(bx`JgQDfTK5dC zdxqA%ni^9FTK5dCdk5O}4%C%WwC)*N_YAFjhSohpyPlz4Pm_anqIIt(2kS&#DMK5d zAqVSJ(1~p2V}>?6Lz|tU&CbwfSCbuOX|n^`>@00|hBiA(o1LZ2&d_Ferp@k5Yn`E; zuBL^qCJU-*r}cC#WOnSFs(dARih4?%dP;(NN{o6+oHjc{J;kM-lAw~3qLPxLlA@@j zXeucwDk(0N6h$Q^K_w+cC8d-~ilUODsHDWGq`1^jQq)i)_R8LY=Pc+pNm+DD^>Pd>~iK2Rvpn8&`dXk`b>Ok+5 zp?4~#cghrW6PqE*&=+Oe<%Ti^mBeznEPYO?>kV5sHGB7 zM`fXo(S99c_&Ubzb&S>P7^~MZR zDqZL?D(EpP=rJnj0ZN#g23u$aO|=5u?}Vc9*yl2ofCmeoF%A7wH2@qW&|z++6q`(r;3=iprzS5G>O+clYS$g zt;qc>TalyR$kA_fq2I{SZ{+AVJo=3s{YH*{BS*hcLBEls->BkDp!^)5DteC`y+@aJ zIg4ugjT-um8v2bgdI_KY!KZ)l=^t`r`7tu}7@2yE9KD=Yyn<}F1C{U`Ev_znnmwJK z_`Gc0v!<1GXk~M>vN>AWDq7jjw6Zzc*DBgqhxRo``|9yKsA3(nODkJd(5r3La*p=3 z3+-!;mbHqOHAl-@Max=K_*QH!a$bwh(T?V5M?2GkI<%lUTF@$5P>0pVF70TJcGRIA z^=UyPWZN;?HlOw>!tc1MonGqG(s(~p1-%-Uv}i^Hyrn2j{9Pc`(iT7P8|Kk9Q_p>{b7#&N{;>%Ib}dj86u|)$SFhQ zlu2^RB<(>l?Lh_YK{4$?n4B-mv0lNkUd&M*<|wb=D6hciGQ1AW*K;uo7jyJ56L&wM zFdKIow)Z#tWBUMefN;z#%*_Rplf~q4`D~%>MS&wY{ydI9#qp;&zorLAVUt*aM-Prg z#j&V378S>08ONg9DsyOO;`QKIRI;&43S+y*)Y7y4cR5G{Lll3&k%&qlgZmpQPwPNPh zikVw0W^S#RxwROpXDXRtE2dph%&-+R!&b};TQM_i#mumkvwEhS)iagMvt^lQ>&ZM@ zXJ*)HSTR#W`&38!l($TE)-^M)l-#N$PY-GPxv~ zTs8HCB$b3PIb4#uK{r~jMk)hIGPw+yT#_6vNe-7`>>p;lA0~TCGNMm1qVGobR?Ub$ z%ve52_Ld}jOOm}M$=<4H9qS7o(AMPlByVdVZ|ltrQZX|~#mpd;vo5EC8Kfw4M-k?Z z%E|D0Gj~+X+)<3VqjKhs%9%Cl*{<@YnE9eC^F_tX6qPemRLo3KIje1Im?MgiHTHIG zolS%}qMpnV6*EUv%p6e#b3_p`%ihcm6*D_jPL|o5*`Z=)hdPsS_9o+OAmi*!KhQ+0 zugEM_yZnzS82M{4z;?nIykdbV2YW6 z=}CX3n1Lx~2BwBwHpvW3F*7hR^4cUbFg=-pDP{(yhPjtsai znyfd^cjL^rbY;G!l)01yePBQz7^0Ueqo>NV)g%>(ZuC)k_E|>1R7T#JB=1a;cP8Os zBIpS5DC~uuk0g0m)6StZ^db40%tZT|O!^Q*A7U8kN9jp^tv)kB zAM*3c%mjT%f<7e4_&rK(wTPBDPL7qgZClqg+PPd&DycwgRxnO0m|!Fur7et62en;0iji?hVZKz1 z--J&VC}|_(jBumWL2cfl8aV|!J1R!iGup1!(WAYL(_ZGQ96egie3fI8N~c%IIP8Eq zPhKJ8V7qhV>+upC<#CSe1jqD`HF%3KQ)KJvJ3Ig5m+I~=^DaX+!Or>krRqDI-HdZQ z#W`;B6>he}$#5_zJBJ`3%ZiZIX|g(ptWJ~FIb?NVvbu1--i+hmAM3r(-|5qu`kHG4Dyko?ndZOWjMcy~16A zZCgX>pl8aJKwCrU(4M=n(of+SJM+k4HdrwmESUk8%+}gSZ8rIwPgdlU<;2NpBsoo- zoJNw<#K~#me<0}(;`9e``U6Q%kgw3p*Je7j`;vA)AFUJ>?$#YJAEuPi zipyxlWt`$t0rzOfea0#6@8|^Y=p^sx1n=l1@91LQ(Zy6UN~vU&QpqT#_FY2NyM%go z36<^=Ml>${gwKd3uWOf5&nTsyQA$0dlrc_<{=;XC<5JHkWsKv~m-x6Z(acOl7&RzH z4SBs%GTzN|NXck2&m|POM4mp;PA7t-s7l$D*my31h$TLZ1<6^;~eM>h^Kxn zwi$!j{)s{)V`t_k7RCp7f%#CkDafZ zucHshwoBM;aw3{;_V#&JNAbd`Xc9=PS)a z?P{Vzg$gL!$>W!p>wlfI+RS;P9Yal!p_Y)3hV#6$T@6o)mO9Fcom7Df+L?PPGQS9U zUWz;~jJxv0g34w)YDlp+9{D>}GpnKtTA1yA<8UOk&!PBaQ(;EoVe%+L9u+2!GUQQ) zJSxmsJj_@;%vd~31{EfE3N!u=lP@Kxn%N4IQ6kLvJIwey%=kM@z7%H69cIiOCOgV! zE+pAem^q7lZvf4#gu|#*^B&by$8@`D&ip)nhtX%gBaLpCYjBura0)e=whN8JoI-xi zeSU6zzDgv__%h6VL4u4c%qTO=_%ck!l_1N?XPy(}S79=%Fqu_?oGMHWJ+Ojq%b*BJ`Wsb9I45L%+HiHzo4nvJ~J-o1m`0y-A==I3i_Ju zD)XayZ|DBzE9$(0f@b@`do zOnwz+oD^o96lRvqVs!i+h>j5+M6a2OS8jH_98Xb>rzmT+A_f0qyQ@T4s}*6bR)n=$5oRNc z3hd5ybu8j2j4~Tp#B5{{M`VA)4ReI!EXo?@2uEAA;3;gB6XlpIVfAu^)yol9FGpA- zQpB+sWpzl5wO0{VEJs+e9AOMp!iwbxYeZrkuhG2s;i!$WZaKoSTf(~K2h?#8tN0MVY z${N=QM|PC;A2E*aC@Vi=9OY3~e#AJ|qpWU?u(~zEaUbQlkFvTo!qFe)=#O&rM>+c0 z(^f=S1rnnrh_VVKMtcyYJ&4jCL}?GA)G6}WxsKH5E2z(hsLbbQ7nT2PE>XVbL;nX{ z*Ut3LYvgf8Zz*!!4&=Hia@`K(x@mIV^sm%;l#%O}k?WR`>z4fj*R^xGHSJThP|soK zZ%2Nms>A$@{pRaAbbAg=+ZLs5i_*41BSG&?JI7toW-8iDMMf@pm6BH}d6kk^sc1PB zZ6^9d$`DzwX7z?<7C^Vl0%&FdG_wE+>i-Gq|C;*0rv9Iw2b-b(uYach=R5If>i_vp ze7~mur)g``h3XBf;H1f#HSKGf_BBoWnx=hC)4rxzs}U(wY1kUPG_7o!_A*U-nWl|Q z(?+Ig9jj^oGPHbYTDvqYT>5AA7Wr8OgT+S#t z$tby;QF4CvY&oOkaz@GJehy7u^VjV(e?`qd&$ND=H)GZQ zIO{xgJGEc8Q~UF*D?dvnul6g}b7-plx}DmusP^l2YQLh|uc`JcdW;PE{jIRim7$MwZHd z9V4C;6^<~K{}h$~Bo&TwDjemEggR6CPf_OxQ|BmWRFtL85vI;jPMxDGb&fD~ju3T@ zFg1To&0qga^N(@1N1U3!{+Z^l+iCuqn!l#zuc`U#cA9^>T?I*+v6OCCL6W9VOS7`E ziassP=&Fc5ElqEhW*uV{{aBiQEX~+U(~qU;$I|p;Y5K7=eOH=ZD@`pYNi8QyEvKAX zPLgVWenxJVYJVNo{tCu$m5kx)sP2?g-APj2DW|$qPIV{A2v1YvDW}?BM-Q2%+FwpT znWo;8q~24`_^*y^wut^S&CGU`>U}3xuGoI%!$dK4k9>tnh906LJw!)(h>r9S`HI90 zy;hnYDMSC0p$3zo|4GyTr0IW9d5$j15qP2eKll?wosvq8Rcp|Nz7UVvIjXu)^^xy5 zYxB*TndTpTj8$5;_A6Ft(aazJ7`fVxtp)Wb?;G-VvoLdTgfjx!gnSQF+xJu#?3e8U zm+xSznVZzCm&nJ3s@;6OA1##CD|FrxZ53}>O+dG+VaUf=%H8B{g2rllE#+r0gSPZIJ}QM@(qV~lgGQs!z;WeLa5MpgfEcY!+nFP5BO)b zxf;GZ&3C71JAK+tLyKr=!wl_{p-nQhLxy(9ryVjJ0g5w)!mMY_YxwyZVUO=6;Cl)9 zUIM-g%}CQB^Yf^3dQ>?*zOR7qOY?mNWOW|-oJTd&qnhajlY&XY<-68=*8$Z`kMCXc zy=(L!-P3GsLW~zv#bMxY=ZlqKZmY$U;x(}m9l$+V0#4RL z4s7>4bZo~MZNRAAQS2_JiNnQd;sWtUajWYm zSUYUo_^eFrI%Hy2jvUoCGAkF48M$j#t{T@iDl0dQ$1$pU{J34SYGUC%YIL;DfVFNf zb{2cWmdr(s%J0OL;&$vs_Uuszi6J&9W=HVILsoi{mka{`cZ)@h9<+ zcn%!+WAsrE%Z^aj`pRL2XL+Io8e9)_v>hoXiUZNBb`knwFBjK{RpMb-x^=k2zsHJt zX;j1Xlfw(o@m?@q>)Ya#<(0lRFfi zWze;|8Xa^8i_z%PeK3^m#o{b+iMUSOB_07&e@9@I1X(0IVb#9=azx=-0q&F<(OV1= zZDJpBh&USR;o0I+alN=(Jc?N}?~1=mtd1)yWCQx#?pSzM2>p9=qK_CV#-Km%OtC;L zL7(px;s$XKw3Ibsz4#0bW8+wFrcn-%I~AT4MhEY%=rle|j1~KeS>hORk~mlVLEI?r z6@L>iiw)v)sbvD|({zKjzjNVP5p*E075&6;F-}ZDB(hMPjDF&miJQcI;&Jhccu#zR zs=uVnqN{g{#JWzn`^!WWT@mWUcIaKco7hhrDvrg`bgD_L|mf?ld!iIG8ws zIFdMyIDt5sIGuPf@i5{M#D&Dg#8ao)*FA%H5%CJ*EyVkXj}c!at|M+Deq%B4h)H54 zv3}ZqbN3JW5nG5ui8~QT6UP%L5~mPn5N8tS5RW1rM_e*}=0OJpONnO@&nGS?UPio% zcmwe^;$6hm#D|HG6Q3onnLhi_S-~5`cZeH_n~7f#w^$4*q9GO$ONeP=mY5?p&X}{` zv`{Z%3vmQ-3~?fHI&n7fDB@z`GUEBf6~t?Zx8bet7rKx5An`Hc)5I5vuMyV~-y?1! zen$MpVptG8Vr1s*`Ln`FVi~cLSWT=a_8|5nwh)IBcOs4^jwenePMLY=w3*=<#F@l7 z#G{DE5tk5`63-%@Ph39hfH{YRFC$(>yn%Qd@h;+O;={zpiO&+(5Z@raL)=K*jJG%^ z`~`7~#fTyrViB=~m?mb4IbtKR7qLHaAaVGi_z~HKIEJ_faT0MV@j&8i;ymI4;v(Y7 z#AU>Dh!-9@XZq~OCB!R;HxTb2-bZ|x_$2W~;v2;E#7)F6h~Ha;c!%qZ77K51 z*0jT-cM$I(-cNjl_$2Xp;w!|p#P!4viJua`B7SeN$eBC0N6(@VF-}YoI}tk*Yl%(7 zKEwgU!Nd{7k;HMt3BX=OlZn%b2NMq?9zk44TueNbcn0x2;zh(2#4Cu`0ectSLR>|> zkN6<*G2+w27m2SC*Ad?%ZX$k0{KjHT%$+}VZpIM2B)(MBIh#0555 z{ome_h`sFhux&BLb8cG<^PGmrRqPS}+bj2oHhz`R0xVi3z-DJHYGUNS0b@Mpw#AM- z=e9(JNU{`X{h#_NsUU1?%<^;DwrJN*m5BDXC06ll+ZH?XoZA+=@SNKgqde!f#Uh?_ z+hQ5dxot7Sb8b7VhY~g!8q;hjKgU70I0FjYB~Y4fg8s7_s?5{SO4dTb*bHsVfeusx zb*37cNgpT~!{tahUQWWw&NJmabP-yD?m;W$HS!kR?YP&I*6ZMB`tkhla(v@@PBj&N zHs;T$>Qzf&YdF^bp+#4AWp#TjRuSXR$MQe*viwi1Ove4_X~Unv#{ADBtWaQ&k3j+F z&xQH@vBEXS&MW+WPT}WSKm9#_{4!pN6Hmt*;{)Sk3crny&xjut&;L!u&xx<(G9hz&hyaL6Z=ryW_)L7Sj#ek*x1sHRles1yF#t@UJJi+N#m91xT%`W*c?Clzc6-U~j$xxdHp_M20=Ub+5wg^bfaMgt83vyXJZP zf5m>@_+Q68&tlIbTkW|5dmh_r55Do5)j#c#HTLdA#zQy(R@GGv=_LGmVM zxV(kBk+)Gb@-Aj`Zb0S!QOIlk4t@21kJ;6in5)e-=32Ab+z%c9Ve^D}+B{>PGtZkB z%!}qFsQRy&*UgvaEAzGa#%wX)n;!xZ48oe7qk?h4Zg`#9V(Krw4(86h5B>fFWZphP zXR?p&dY^XI0A78;FTVbzW(B(ZUuG^hE6pFx73NBF)xUC0<}KtN-Zt-;cg=dU!MtbQ zHyh0d$V7Z(HkpsjX7h>p)coCiW8`Ct3r_}}_l{O|nl{T~db#~5X_ag1vwn2BbgIo4cft~WQB8_iATW^;%6lUZf% zGS7S^2jxMh zpdzRYGC?+|3OWZ}g6g0q$OT=4+Mq6|4;q5TAQr@fM34-MgOZ>$NCiWKVZoGOzhG)G zEtnqcAIu02z&lxvck&CL|1Z9iF?c5n@J@dI`Tx@Ux$0l~od1<~HU#ga@LjZfCv((n zwNZVbK2#s6P3mK{S$(2DRex8Xsn69H>Pz*N`dWRXzExY)cj|lfgGQQEy^fj_2a6hd z+Sdj>+(SByRT`qY2P>E8kOLaT{UOVXuU8yrVtE+TpU2XSb z#fDm4r|WftZq(g$lkTp2=$^Wl?ydXizPg{@PWRW%dI0KLT99qT8fLa9Jl1d+uHHm_ z%PwlI-c^s(qx5LqrpM^9sCL;+kJr2FJ@lSfIc=Supl9fbdT%{R@1rN{ef1Q*pPs6x z>FIiZeSkhty`vA(2czD7rk;frAP&`s={Z;f;ut+o&(}xjBlS`GXuSZxFSPSaP(`yy zpP^6COY}+lWPOT0RiCDp>Sfq>CTeR|>9bK)^IPPJ&({~|3-#~xMf&%8xxQFmqA%4e z^dIzP`f|Ne|50C|uhduRtMxVdTC6E?y}m);sBh9Y>swHdbDO?h-=Y76RVMDzck6qw z!UWb-!-{Hu(f8{I^n>~#{a5{n+JKoYkLkzt6Z%R0lztkkPCSdso#*un`bGVcUZY>u zujp6xYj&+Q{U)k;-qP#z+xi{-u3oP<==b#ddZYe8f2cpwoAk$ev;IVXs{gJ()1T`v z^q2Z8{k3{uf1|(ETWs}@{sDDA63g;w$3eR|550Ab6F4C!>_nWXQ{=>)xRX%pZAB1P ztSEIlIAu;pC+(E0cim@^DSaNR4~@bq7HwF)Vyrt3S=RCH?(QB~zhVN`i<;=}jk!r! z(H(i$DOfFPs$C@tH7hf0tqLkt4o02IOm`M$D;;XruE3g5Si1r#yU?}vjW}XPeH{BR&_$H3f6P7YgAx`inHBwP_=?}oUrOM)^S4B z3RZAJUwHI{=L*n&a4&N&cUL0si?y3jyK=S7W}|KeYgM>6Va29f+*{EJ{&v@{5WNcP zRorFQig)jI?{oj`u6F-|Oz{J#V|fVcR$!ebtXT0FYFHk}+DvveX{;e{yUU}VzRIrY zgQ|`itV_`qbsbo}2X!5dShEK!$=lU>{NAX{?~9s_?NHItjBNQZe>m3W8H~z~A^!IM zK&(A60_*T#^&PA;F&Y&ec9os+{_Z%}KB(ZBj8!Ri#%dH({Qdl?{xpBOzrR1jKfpiG zKgd7CpXtx?5AzTA=lFB|dH#I=2>(c|L9xI;#$V_k>mTPI?=SLC@E7|h`b+$i{FD7t z{8RnY{H6Xf|4jcZ|7`yp|6Kn(|F{16{)PVU{EPhG`^)`{{Y(5y{T2Qn{LB57s2I80 zzsA4Tzs|qjzrnxJzuCXlzuo_nf2V(!f46_Hf8Q^zsQtu0uBDB&l0U&Z$)97Ld7*sF{#rJ)rWeaqh_B_-w@Wr&R(UK zs`dY>jYDnSzh>jMwr*Yi)Anufj~33_w_mqzuxxw%s%4vDZQIW*+u^irzi!!1&?o+i zWjh--&04l?Vb@muJC^NXYuo<2E!)3p*M4r*^bS}#2&-rQpR;Lp9sBRs@QcJDG;zCZd`_*eeS{$Uj})X&}O-v%pq z2khXgf+f6%w(!sXYX2|4Xb~UxpYWgbpZZ6;=&#upma*bt(ckQ7c0%Wt>FC=s16^AV zG;_>cGtbON-QMx2+B?BKR9Nl)RICBN^rsc?&-`i4`xk;2gO`Fe!OL7>{y}$ZEgS47Wyt1j%)pCYv4~?gMQi?{L|KupSFhnxD}wc71m2b3|f)H z|MP!}CheZ3`2X})d+hsvaH~D2B&=A~ZVzJE3aBL&_uw5AVp;o%;S>0`=++){s!)~L ziVJ@_N>xTGmSF$9cE`zz?}e-^X}_m(JofyU)vCieraK30qqra+75txw3I1Cmg7)!% zjRjWyPsIW7m5=`{0{GwN|Lxd6c-OxaS^kHtYwLUHe}HoZv)jj%|0&M(>x}D{`PR1Q zT0f5=^Gxf%>E8Kw7}hVujRk)7(_Ql;yHanf_ps`t)i!^lPtWU{|38}ihiLNuT2%eN z&OqBm*FVPA?IY`+;GX%I`k#-XZLI!}k@~;I=|A$>e4PIO+3bD(e}c_gMdKHBealWm zKk?Il;HJU8!5P7sAqP2+_d+`O=vO%Bzi9!oY?GXcHD}*}8n7Aa<`(!H1J$wwDrFXG zWFwTv{!kf*Bk~>t4RI2*!ULfT&VvTH2#Vh_SfLA{=BeB<{pxA-&X8_Xv2+z93Ym!bPnHP%_^fq7vq zSUF)Q=xXDkp-q8aHWS*|QP9DbK=V2avEXuOSyw^1x(zDTYA8~V!+x&8tf_Y}V}G;z zg}cR5p7Dyj5-;s#y`0z28}5zsreMDBLd;yc5UU~F=H27n?>*u@={@hg;;r@8dmnnA zdf)iM_xy;T^vnE8jE#CIR{fw(*|l9pKy@0;C~YF5vKi2t4uh(6Dx#?KpcGvKZRi@T zae9Y;58|Ci5ZgSD8AWT6H~A2mfv^1Uq5X$U+@z4>>5M#3lj(!3(O|4`HxikR@yH5H z!hG84h*M@Fb2JaS`XbDiScZJZCCF-Ahit$qWCb2XrfLmxM(-d)vDtiKwjeiPkOwG1 z4j>D?zcJ_)^bZCG!-HLdF~J_lPfQIC3}y%Og2ln9xTnqwE(%rzR|MAuw*;$#`+^69 z$AYI3=e!oI!@af%n$tJXp*+Ml$xvCSGSoSggXYu()u~@-0Ol(V4UGux5*i&E7uq8< zF*G?eH8dl1aA(GtO+GP z){g-{(@y}u*DoUa8swDzcuu*_*^{a*Q>)bD}{k`7$Ph55*ew z${*$;4)lk+uLB3TZvqFSqpR{;UA$9&2xdbme+rM>k2sY$jX0gSKXC@}0OEnfgNTO^ zXA)--4xkDAZy?@CyxHA=Z|7EiQ@0cUM7)!D7x8Z5y~O*7_wze_fcPNs zA>zZtzY-rIK1zIy_&4Gc#HWeR5T7MJM|__60`W!SOBREpuu8NtYp@zLx-k0vgpaIt zGcwWTut@J24|(q%ut7P~37O0H@mo)1vAdcIWHC45w_eC-*P2RXG(W&^y^-CnGZ|zz zKg4f+km;^BS!6mt!f$<%^=>d#uyUL5TR&K}M$;J?(2w!kcF2ZzGhL7k-HhM*BQxG) zs*xG}1oQCT@V6L@9b}r3C5Ns29{86318^M*+?4;ek-&EhW4m&Ch~cn=a+E^)mQSE*o`#{k<5dO8s$es@s|a47Qk z2ca9Vi>iRbV6_j%%JvGG-37p-%&`TO{QWv}DSp4+tN`9%{s6quTn4xK4?yl(UNb?p@nRP&I-p1$g6g<=;fn(mm z9@oIC1)f|GEx_8xl$^%8HIM}vrg-43GWBVduioowz8 zIshLC%770C9f3~-Y2edAIq;dF6Y#m90{DDT349^Q0ACEUz?Xt5;LAa0;A?>$5w8cZ z(&o#c2KZHw1AZNJ1%4CM0=ERvh)@qw5Bwo$z%_^fBPfWOkAZQs8JIAi;CjC`-X>k@!|5z7vUWM4Aow{~rEh9N>G9 z_!cBSdx=k8;&YdH#}e;X;@wKTM~QbR@%|*B`PW@ai>7EA(8 z4fX*}vsDtPo0tw973>cj7t8?eW@`?9Wk2V4;v5oZkbM5f!AUs!li+0F--AdjF zR|fxC1N(iei}Kr*ML6+ujAP=cmwDLZvy>bEwJ9Izp*{QFA7ygm_@13 zH_%oYp?4CtRBW(V0oGh$|AU`?i8;})y05!$Vvhet%zb|8pMJcG=e2*lj)QvS^UgB+H zlN>0ISLLcv%~4OGPU8gBEIj6v2G0a91h|t?I|m&acdh+aBxYMmd@s8jsMYA^^@_V5 zUB5P=$Il{shYtMG1|iVneKtD7ylh@Z$C+=;H<%MCMTvX{g8t!ZwKwaRDwphYPzUug zj#k3m>O4=FCkmZwqC8s>&L*)sH?Asw9Dfhl-{GN~pkLdqa#Kwu8&@UA;s_;fQ4RJ; z%==WrN2SguxY9B3NFhWF3q%;TIOs-=N}Df43YAJjL?bF=CS#7T62|`&epA4oO%Nrh z+?j~JmZ(D#DMS~47GHH@J_VO*>Z<}euHQ9GlTDY=> zW?{Q4y98HuwYgnX{>#_*P{TS4vwbheJlsEGR`P|IEqp!Z4Bv>y3e2#*4d3k@ zm?{28%)tHtvu!`ZjOte~XZbVCI{!laAl70=@j$Ur4wgH}TBnnADbL4x5+mgu?u*{Ra#gS(SfEA)#|Fo$(ZP~niE2X}c%B*)oFAO8#s(J#7pif= z#la(%7o{oq42JyaX2RR@L|LfzCsq3)sXYG$ZssHd8R&t)=tzl}x$XDs>=Np?a^qZ8tM zk(Ebcjn8US=N>O|@&tLJsFx?nlSMc5DOxJJ%Vnso?Iq8aXN$h_Jb9t$Cohs0iS4lh z=uN_|VSa}gi~dFTiQVLCxmrv>7o+>dUR-f%BG#IELQIlR$*06rtTOegm?qcC&Eg=e z9{8D9AitDfieu%sND3Xtbv>6TSGnS3<-^&Z!gW2Ds+fw2Wh#j}-P6%eu2h_%I;wJU zma0%$ajxo&!qE#XJ%@gBGsW}lC-(v@${g_`*R@)M9&$&ESJW|Tp?D2ll@^OPxa#LRtonJX zco!8POT~Ju`gxYvpw7X1pC7Wf+(+mtcf0h|pVYlFj5R(Vmr1Oz@|4V~XVf#YvwBXw zAiJoS)JrmF`!C6^&RuR?Hb8ahBp16GH!DwfySO9dnYKfsywy9z&&b<&-yFpI=3w48 zhw#3c$@^v&@0;1YZw}>sb2#stIlOP?^1hkJ`({4xnC> z7*}#da22lPM!fcYcum}=c(rxG+rc}*yTN+A<_E!tp;D+D=}>v7Q>X$eM<$dFRflRq zwbruY9)gE~rFjo_W(&4`jM3}G2l!4m*qS)|SB&WSsKKKRMB0VR(TV4-;<+1m?k1djspyBwgJohTR34l! zc1E?qAH-N&U4Zv;m474N#m)ZBVt>BJ8GMfi@I4;L_jnNB<4ovle-pD&Kk$S&3iSgo zq3Z&!4*6(>|BmhG0IPVX97^l=6#mlnU%pE={CoEaUdIKqIadg9i?t$VRa#Mws(=#F z2}*4WKESTkw+@#YG1PSX}gugT+PfI9Ob{o3*%@K>>>^ z_p%mOPP7(R9%?NvdUnF%$|J1B#Tg%x596$`xR?zAiz^q&XYe?|+FiLAJvukz?6A8y zAMCFDy|ue?xq?J3FSB-6UT*EKTxspDywciTd9}5>@)~P*<+ZT8X?eZ1yYfbBcjc|t z?#flLyY=!;Yj@>c*6zxeti_dYTZ=2-wH8;dw-#5vXDzP$&{|yik+ryT6Kw5#`LVUN za6ST`bwq!I^43gR{T&4Cq6p&;bR7yI@*%cu4T9ChVJ!hX$*< z9DlhM50|{ku6uS1EY)pz81ShNFe79W9wEdKpNg>9f=2|g!$1)QKN%~E5F14?J$otrbqW#45uyyKbSZnR{lA~4)W8M6#m z4g>=qChZK3UFA^Az~wN@z~ykuz~u-V706vIE0E(XE0DWcRv^b)Rv>q`tU&HzS%KWs zdSW@jvI05LvI4ocWd(AQf#bV1Q#EJI$#Yy)1#mV;i#VPjs#i{oC z#c8Yux{KzbC)K{y==m=h(PgNIYy}G(Zl(rf&ECPgxC7?Hhn)t`bU*yhOBk=8!AdJK zum*6jePI7B2fF~>NLOKW{snAo-(WbnGNJs8*`w>Jp5KQ5bpG2D_RO!A@!+vTZvDBXLK( zjrg{Uco8FR3+6yQgUE9YbVgym_ZFDvsr_t{b5t9w=xEH4`oUa`G5ajp>_~Gq`a5;T zjK){h?qIn`pikPV<}|YudgmbZpzMw8;d0A))vM~YU^lFx`5WX1zlJKY3%>8M=ux&n z94n4T|Eb&1=Sql&(77v$K4X^qeuz8sIq=^P>^OtX#o8rtRLH@9gSa7&7RB7VW^tT6 zPThh}>mGbskHDWjqn<}b`F*tsGh(8sS1HEqmTt%+_rrXZp{Qk7NRTfufDr+jcS8i81q;ki~QI&gE?p-;p^6<)|D^IFCxAKz8uQPromMP6tWV&X$ zXST}>%IuICmDw}1S7z_bKAC+p2V@S-9G;n*nV&f_b9CmI%(0nMGG}Hk$XuGaGIL|* zw#r!voFUd_Cjc{}rQ=CjQAS(A-rQ`vO3GTS+u%l60)$PUU5&+d{vEPHhJ`0Q!f zGqTrZZ_M76y|*eRgrkf6ToHd=$kS|35RgyLWp@NG`qJn5*cU&(Nv->=K?atZQ)lS#wtj5(hx8i!6 zyKtq=3%KUx7p&2zW0fA)sTdKX0;}{juzPP~u(1?&pQG*mlwbM1{#3u;AMtnb*ZBMR z2m43($M`4vr~8ldpXNW;zXo>S7!UzZASI9;C=7HAR0MhiqJd$7F>$+34;&Mi7dSC+ za^Td!X@N5WX9dm;TpU;xxFWDIaC2aDU~Ax+z-xi+fsX>82EGXV6!@*sU6@dqT9{p! zUs$N^-ZO6ZlMBx%JhyPEw)=zo*?oL)BJ92jcHgD#Zo=+9*gYq1_akBV;&yg7%a4TJ z=Wkj9yWg?tZrB|+vm?9j#q7m>CpU5OBv$%qZPY){)IXQg&)7ffS?g)+9|)i^ZF13_ z0R?99LJd)M7U3cO2T|4*gx@G6@JAd2x>&{>B>c?IEcTLzeb=Uk|3wJHv zb=j`kovU^Y*|~n#f}IcZ7wpO<-9{NL{R^m2Fc?&o*c?=IN&%ZaaSfcZ2(O-m>$IoeS~G z0_9&W|9ti5r|h8py5dc;gg6{moz6IY>f+Kt?)TBJqM?!4r7KsdY}Z?`b&VCZ23kX` z5qLeynuecqtyA&yeCs0XYHPE#)q+j=AHTAmvYx@;X=kziXT5~q2j@}9(Xz#@SpX}{I$LZ*Sf73_Fhx9*hCC+hhj+EmFi2b8I8gzueD|S@tf9R+2 zEY|;@^vRyp=n>JQV*T@RyAC7J&+XwxK6=J?JZGYJ{R;i<-*k<&{krE}dzh!r-r?zG z6d($e>`BC~n(^p6O|+*W-Luo(A19!9AAx>xk<%~sLSO&1?i=3~k9$58+tD{t51A~x ziBB>AI|`$K$ru4NV+?R2Mgg~B96+OhZ!iwPC_pa7IG`^^0^KkQ=;3(=_e8%DqXCQs z)DxI>J|zk;TD9bO;g@qn7tA0k5zVZU7vg!X=OJRbT=YdO^Dud*7%Hz7`uDn^d8=3D3JI5Vxr!@tPVYwyLh;WmQKb3-N`T zf%xGpL=uk_-y`byvpP-ugn7)r)G1g~xj_7dh~pl09-@VJ%YeEQ(Zs6|Kinb<)a|lR z-6c!aL$Xpmi93S!Lws?7dQ(ObVLS`-uyVN&j~`r#J8X7G47aCEcuLBAiIcLF@`Rba}m2fNxY-Rh%`A}hXk7sPz|p4co`ihI-qIY_-N zGUZG$Ty7NqR6P*~9xt9yy~LBMx9p*w$1-M6EtbE^WokNln`(J6?#j7WosB1i953Gx zWw`HZ38LN)i52oM@fqUIUnADhTfK;Q&9U-0d72u9`(Rv-$j*I;b}T?l=Wcl~o(AwI zA~$d2xh>DCnRu?sNAg2NYyKris1#+X4k`)pmu!`#idBhhP+jCmRgY%@%)`~yv(@p4 ztQ@0`MXYJGT8QY-9{HfWUtNk=$(48(zo1<;_ zCHBkMx%L@iyRX@=*w5Rq+ApI0eFk?4o^2oJjCH@pxOb;L$3D%uPs3dM1be=Hl6|5* z5A(Y-Fzb61v$}+nYma`sxAS!`H z_ffyry2|{^x(quSFE?APb!Mw|g}K+d5_i77!9K+@ts8NVceiyDb~Ji$pLeewVb8!w zd$Q4JOf`A(68#uYhWQ9j{Qb)K+IYfv()b2DH$TUY%`c6ecpB!Xcw)?M<3-~+<8AEYoN0WE zU7z0>v#^`MVIp#z>H|8WfOY>wrIp!3j75hl{8VgKeoQ7w}3^j*g-hjJ~ zn}%_gX&MVn%Q)L~8|RoF<6Jx+YPdPVIM4JN=bH(}A~Vsr(ClDbWF{Mnjf+j6vBXR@ zmYQkCGBe#+Ze|!O%uM4FJcTpGSZQV(tIQmVn;EOkTw{%yXRI~zjZ4h}<1*84Ty6%8 zb!MS)g&8!iG((8Hg^l%Q#JJilGOjU;jcd&kV}sez*l2b#t}{!G>&-Ib2D99_(d=y8 zWOgzBVRkh(nH9#(W;f#&v(mWLtTJvhyBoKg)y5rWjd7=0Yush_FgBY#jV)#`<8HIJ zagW)@xYz7!+-LSPwwnEo`^~6v2AazY%p{}29Ab1a2OFKuK}J`z&c4IG$=+G1ILn*kE<^;U*c-+k;Ww?f ztv9TXv6r~Ss<66Ql~_G0@CK}}tnaNljARE{gRz?A_ZC{eTYtD+ZjUv|I?S42O|(XP zgV;wKac8*G-I?w*cfcBnG4FJ%*_wehr(Dk^o>iVTp36O#dDeO^MTC#${_{Qcdj5YA z#ow?;xCJ|-UZs1=7{%Ci^uFg6eJ`3@J=fY*c4xb*-OaAHYwYfJ7rVl)^t|MG12Kp9 zuv>U9MIr3Nu@krvyMp`JeZ@EO1Vp#D%Fb#Vb_O3WepDxmQ)HdDn@FS)WB(LAZWLw| zN_Vj$5QM@^m7(}hknxO!h8WKTXqdsOm%#lOH69v;7cm|hs~0m^H4~UAX*?65G~Vq* zyDp#@%@TOxg22j&1}pLuztZ4YJp!>KjpuYI#?l1tT_X_5&|qhUz}#A393tEwLumvK zJj0>A89d2I;Cf1p=WS?T274C-?m4dU(2S!$;~58yGJ3W!0IvxH40NFUQMw6A<6s9G z>({k|W*bB^AX^Yef?*CPKq;-nX@K%fECclpmp~gF)<8!(Tmv2Da2=G&gSZ=varh8Q z_9Auw${X=H81L{Ml**O(6Hqw;>4B2%fTXe|TLVdDcsRfe7EebM82M?iDka1e#zXm? z%HV0h0(VWCm~1%7s$-0Ys<; zo*kgU-MoaD&tPYOz&-voitJ{jjjE8J_7Gsmm7c!pdP_h@$QPGWf4G@VGh{9?-9VbTNs=C`&P!thtlT)&-u{X8SEbtSj*9PC{K4XHr0o_ z7)0I$?hB`}sUB=$u*Zn~{vLd0Z@ebk0mSYF?lz{uGsOf}SvB@B==}`#iV4h3H1-bY zgNz652%ZX{F$$m$GoD4zM;K2sbQ|MIgg(l6QlO79n6(P=xI+%~pAIIJe3VE6PdbF4 zPdTJRpLPg9pK%C7pLOU4ea>M5^m&IN&=(xYhA%pdg}&rKd3o7^e2V;uAm4e_;T-5| z4rCLuDX|5-;XrA8(}8UNmIK-HZ3nXdc8ABIRNerybb)v{Lw|pdkr~kU8EHd5U?ka! z-UB4r|04!7e1RuuFrPv{VX(tS2>Kn!qo7pAKvFyS86zh{KW8M><1ZN54E>Uk$3u5A zlIrv>MxGDd%}DZV@=+kKfqu=%>!9B-lIq8|jHF}VF_O~qJtOHme_$ki&yS2;3jG(O zN})e7sxS0s2J4psdj>R$Z1O9k#zKE%R1fIy40iGf%-uC=H1tnKQN7>8s4JjfYC?fQjVOTP7tL&_VuS^CIiS8sgTdVpu+ha~4wTLVG!Bq% z2lyxTRl2{bgu+)e@NMa3@azsD6C5stCNea3Ab--F2kpSn*nxaYvmBbj(AYux94>*T zGP++(!)xMBkj_wlPkyX{tz;%ceXz`OxCNTcP`@hCMriJW<}x&1ka-Rpp!p2-W0Gu2 zya32vfciZdaCi<{$WZ?#gAOl2Lk#tSGVJgoG{R8dMSZ^pbx{^G)Yl;f!MqFY$RHjr zWG9E8p`{G&>M3NI1N>T+Gt_s<&JL)5h`DI+91=aYpz#HwVnR|m0X*YHP(9b|fKqt_ z>K7%IG4Va<&LBE3WVHkQUDhy&*b7+;dVp5Ylfhn1A$u{1y9r5t0I+*g(5kIoxh4N1 zKLEH>slc8BjdDZ#GuYcBWRyY7PRIcao@yiLX*q;Sg$`nLMUVdLv6Vn$slSd^q$R8_^z#ZJ(@uzQOGe2 z^$#=-BNU};97F9fBB2^Y90g{58frT+BFT zYKwJyOyxL*K?GA^W~rfe4l_;-qM8CTPYtz&n1N~#=@fE0L+vHC;{>9g0&zeMwPA?C zX%G<=n9*veokN^ZgD9!)GjzYP2d|H2)OpZj9Kuklhd|v8rMg5={hQ6GJE3zN$RCbp z)K$>A4k73X46X80Kct~DozJM-p=1l95S+x&x)kD?4AnWxC!jSeL<^Wo=mJJP2_@SR zRA;FE5ai2eFxcZF5SKtxnDdTz5CuK~?D5Z~4yx-H~dhUN#7+6zE)4n$#?PoS3rd?%vd0`X)Gtr5yA z8HGO~f~`@M_w@{7<=8XFh>cMCK0xDQtSD#@N!PnU^p21R@S4s8G#;dJ27x%cK$Jy8 z<2giFG>E+mL|!yBK0*XWgMG{bkr<{Ibd!Sxy_rF@USLH-L-RaDbQns*ZH%Hkl8+L| zBx2+mMdd(sfN%ryS0W2+W)yw@7Kb48Ziiy%Jq|U{dl^Ofz0YAVbSs0E10nBcXr3u4 zeLzfyJ_sHHkAsI9gY5Z;g8|*fDDsg<9ms}{F^ZJl|4)Pj0?;QI!w-Fu!5W2-Pcc}n z5H$bPb5Kg>GY(Y#lm~#d41v`l4fcWxtW9XJzQO(a3pj_$^F@Z{Ls&`DVD&@Dml=xg zNJ=Zw2wr7qUMOF4_yYPmLvtXkC}>m#luks@YDu<65x|2WFk(e3KPZ``POJJ3ZnFalfq4*P4(U_B= zlutnObsA4=DD68LiU-ixT0{2U&4`uIuN+#SUo$knq47F_H6wwDrH1A@SOe0ikq+Op?S9a zlTi`q9!3;G|8gLk{LN4d59?seNzhh?=4M!b)L`vPhX)nDR=@|8WaM-x4(b?59@NE9 z-$-p4L9r-m<8(W>32HGESE4bK9yg(#P#%Wj6v}3(tdy6*bMUZhjv;#^!mXiswn}n< zJyi#W=C4$LG((~AQG(`e3Vx|M3JPB&XwIf6Z^Rss&d{7d!51~xKrQ8MC&zFpHv}3@edVbsP95#UxQub z0ug>@0u<${%O8G4<3A1RFs^9P2+BtZL*pQ7M+k~VQXixHnT^m=hT;|!=h9G`%Nd%Z ztIiJNq3}h5;u zv;_74iqc0+2lU;5;@OJ64+#3aJ`9a1X#T9{(3e5`F*JXrepGWdH0tm!bO1wfK{b$} zc2tqA0mZY`V20+PG;Y>oXR`MYhT@}YC_{Y`jl~H(F;ie=Mnm-#D>WL5Rj7K0CTIgg zF$QXPG$%kuF*I+X`Ml;u=op5^k`xQTDiDuZsmuV41r_-XL7y>^q462TGzl8lV8uv7 z;~uOWX{aAojSlBPCp#>FPGP8@r?>z?eLKZQ3F^<)G=}Se4b$Z}9p}7^*Ub#| zLo~0@a|^N``7WS-l;#f_IyQ@;{simt8tP}T(xsvPM;*gZ8?26HsBKoqF*KJ_vmH{P z)GiPfpt2`AfVmDyP$~l=2h4NGhLYWhEN~(?2~b@;nW6a^%}+F4prW3*VrjgJM49&&WMu%z8>%jGR@0-vY7@8kY+>M}lhPsKN`2mfO zHFSKF1KICp2P&&u7#e%3TOEAR+Z@uMw=*=i!yXk4&HZSOO7lo<8!8)MgrSsAV1%G| zGe!h@FJqKIsmy>;3Vnbv%ApT3_Dj%*82dTs!;Jkb^by8OpF`yYY%1%=8T$q3GmQNi zl->idzgZ~yEMQaF(Rsjr1^OamKM$qv1~!#D{SDakclsUJWGi| zdm~=McL`igAzX^V6%@jS69`-#AzTK7t0siYVsPDsaJd;=Wg%Q1#zy^c*$l3@5H2r+ zYb}H;fx$Hx!j;J2$_?R4V(foHlNlRz!R2G@bD*h=jk@E4e-k$9u`83YZ-8boHvHU` z&Dgg=a~S(xXf9*l3e98eo1yuP4gYo(FgASCcrSjKua0>JShBtu&;uaGxj2AXU0aG z=jzJXXG1F(dmFSHW8Vp_WbE6a-5K0XSh%Vg`w?gjW1}9sdNTF{&|ZvvKD0MuKLqW= z*bhScGWL_ufsB{@qK@&BUrc4}jnHPsJ{JmqCCopd@NdFe358D+=ATgbI$^DXqTaer zflW?@I+%O#dI4jthMvK=--DhB7UI2YpkyQ9Spg+K1Lk%p`6a-WKf-k(<0b#Ph%tYF zE@nK-p%;Utc<+x;`W=`*LoWeHyZH;0(h4m43`!Gx%6tb(zKc3x?uL?I!e`Cjptl2* zl|`R-7h_SnHZ#@|=oaui&Y2B;fw3sPFESRT>m|lQ`dlwF7Nz|a###n_m9Yf$HSjt< zb3OD8@D^T6=-U9cv95tqx`A~S^j*gM3rh9{)@9K58S`)G2aI(&^h3sMfqukT>!2Sq zW-Ig)#-cj%DP!)1?qIAdp+5rH*xCRk`vS{={>oTnSF$6pPl5iIO9*|z^CMcy3-)VW!7Q-$EY+&>R%3%i7VV+| zhk*o#CTOAq>WY!%&>z~tp#hrgFdmu$(m*$m4l+P3$OJi{H^_Au0L=qoFbG6I5f}-I z9mYV>7HLL9JAzJNEGPwKU?M1Ym;mkUFa_ELbOqBv1*ifKf$o58^EjvhWWT3CF9-On zPAjDgergN`bznOf;_x1Hr~`b>7zT!e_rVBI58yin*@%Eo>AX??z5t^D<>@Ox`J_BO z0meCe10|ahj{?dY@j0M86Q6-e;BfFIpmLoI;F|{JpV$qkjENV)5f0Bmr#ZY0rSc{| z1Y|E_CTMo}7COV>NGSb{_zuwT#H@CFkJm@F<7m7legMZf(C^1O{0KeHf&M<*;a|`> z4s`DE4nINXI?(%0aQGQI&w)N;zQZrj6CLRDPICAada?t3_9+g(K~Hs{?^)o0`f8l! zK;L<~1M01@)FBF8=I}gpxx)j{6^u##e~H6`(3Oli5W32t2D;keG3Xix%HLXtKcSa8 zP+l){*aN-Xf%3i1;VJDdx>hcRvFz2H8ic{p^d!+FsA8Pf};@+Qs) zRK~zefIj4~2>LK%CPE)^xDdLHF*`sXb+`yhegMp5=;Pp@U@>@tF@4Y{9hN|!V$4+N z(+=dP&oE{h^jQb;+2|Ai>_C3~3S(wLUuDb`=xYw- z@2@jvHuMdLRnRvXGY9$>cpLexhi-RR4Sk0(bD{4#tbxAAn0e6m9o9lWV9b2zhYpuQ zKVr-R=*JG1K|f(kKlD?F%b`0MGXVX}VIA~y#w>(>;cx}?OU4XBcRE}N-Nl$8=xzt9 ze_t_X82YutdgwQd8G(N5a5eNh#w>za3i#ZF*`$B9d3f|Wy~%p zd%;lKK<$O5D^xMmPEZ@6sel>`wHeeFXu3fyhT0FR^O{PihoQEF>b9l|>Sd^1p?a(7 z4ozgJjWLrPs-YbiYHz51YHFY<47EK}KQ*<`REF9is)w2$&~%2{B&u(kp3qE&+9#@4 znqJUshT1BsGn(GeT!z{$st=kz(0qp4F!Fm%UnqQC`}=)R__wAX6h2K*J4b%3=?{hP zYX7|-3V+o^p%KP70}8*?P+7qjwLe|}h5u>De(*W%cT{%pGtCevd`tTm`2hS$GZ+dV z(tZS=A-~ZKg3@__#(ZXsIw1;1FzyebbHJ&XL&44ks~IoKqTn&c{Sovj#zr|7Jk8iQ zL7!o4luN<0jC~gLImU+n7d+44Y6(HlgwfB1L0sS5;M!W|d$k|ISj38T0VJP1cu_-{&@`Tm-NqP zD2DDojgeITr!(pv=(&t6hn~k!tki!#L;LLfix`Tt`Y&K;pPipR7f_tlzlMlk??^m<0*LT_Ls+3-e2kni8bNU|}NJrF6-O^iGONgh=NNGT^m#^p3w?nR z=R;p)!tqJ7(w=+dJAY>*Z&41$R2Mpw65!aixFg- zw;5XF^>1ec+36id)j;261ljOCMpZ%IX9VT%14c!mA2NdS_YtE8LO*5%<@FPW)@%Kr zGJ^8GgHeN_pD}`L_Blgqzy2>6nGXGuQDot9D+wuR%$Q&KzUbD#jP3!y{_ zUgI+Z9YI&TCanPYP77rc=mGlUb#G`C)ZsPq8W;vpKdhn9G2k$~Mp^=mj77($FxFV; z5sWns+Qe9>zk%uCXd&D-^cZjgKId@gJTM=x?}DBPPQq*0DsVD51+QO&o(dM=^)BdX z;B>r({Q_rzGx3=cdKOrS*FNaEU@^|QA9^uZiPvP0RbU-nZ--t1HsCe~>LKASgwp!~?sX*sk2Bt4DB3H+8-}92BCNNdPcqhRvS z)SJM&;5{6NKLy?gAK>*M=tqnlc1C))NN}56m^=gMnh4r14x%Q2>pTaMxZ}2ZYran8FxDL7sj0l{f%)` z*-<)xo9yrh_!FOxdJ@JO+&=v;wCKG{H#+xfdp_}ns0!6wB4}7x_b(rw1fhIDZ z%b`h(=Q3yqkc@NILQ@&sIa(AVO@wCw6zL{Br$Uh*!hmo!s`p5{Ta_x=wQa8jjOP{T3V?F(909$Z!QHn-;a!YpJ@i4wb1U>A#&a!n8{@eJ+nkU$!i{tW zQ4WMX8VY+8_82H^NZ7Dj5NRbmsKdc7j6DiE9!$V-lzDI>n1b=r0ZnE+tx)8Lumv=g!5z#+D1-4l3(a9XPeF4T&t9mX z@w^EwV?1v`hcTWfps0HU?iVLQM>3x6&{+)boGwC7GoBZP2%{Vb`*3IpV^4y1Wb8&L zzKgIAgTBJplZ7aPpAdEw`T~RdnTwL^7`r#NrNJhI-468W6ZSFCr3|h_7n_zb;zua_pP)TZn^rL5 z6zHvtpyPKivJQGDgX`GErn|xYXg^_#+r~5Q_l5Wr+4vOgu@`OoUlPD4X#Yhk%8c-= z6Y}R2jj$&OL!8-qy$WDO%q85SNOTiPQEzE+ipMl?bH<7?8T18BU(lyAGtwqytUb1>b7ivEn_OL-8!pK5mc>zN(+~` zTyjI}U)`sFblQhU*3X`E#~o@*%jiAwm{!y_+@(%`K31bcum_el?F(rqe*E~^@#7as zcstI~=NtL3S&GOOd182UNUpRDIU*_1ZMl?m3)BL)MB?O3kF>Bc*itjF1lm|{N*A8{ z-jI`H7&&=4dD&U8Tw1CR_p(dwXnQltWJxVUU(5X{;Ir~dU zq_`*&4h7Q-(*wEL_?pzraFRE(EY)37Q&q`TBJ9+LLzUX^d6}K6t9q7})Rh!wr*}VO zR`(=VXLlL$G& zV+7{-xRZ%+%@oMDDHU>zd(EJB&rFhDVY$854B_!K*4xsJC$cwGROICeQBhe@*|kgO z^0LxS9rH@^ibI9?rktXpq7L4yvQ!J1r`%`xRm@34#U;hX;Y{{e`~vrF=eO@nfPyj}fW6FK zEmVbF;zSGflCazi?D}e~H*mAA$*LhOEiEH01FcMYrrVpNE3H{lTb1Q5X_sxe5!JPI zLC1^xE-pD|pS%r4P5ra;+_{~{wyfGWAG(gl(i^rpz5CVC1JdpO-_xCuLFrCIW8>Dz zcFVSAW<6@;2%YG&rD)&43`J#}PpKZ;vZF28aeEr6s35tpb&;4Aok8_5iRztKN>^@9 zc7|cei1b+I2zV)u%XzqzSxIXyx=8$rO6ipz>f#0$k>)q1CV=)`TVRZ3D;i6Em zFyJr9&&$f>T9|-ZC=OB!(SGh<3@i4jfUUnAq(I)c)o zTfIXH^9{FAh9(E~&cN;74YzJ`;`I;SGc(~}Elz2v5T)g%<(-@c$6w%7xWs>3;oJvg z_RT|AwK~rCqV^?v??Ecr?t^7LUdKM;x+GopdU>a~UU;@BwfXwZRTzcD&x!RhE_<2$ z1a>o>{`Do)r)2b&VNonbM;nUJKuNUC=-Z`ehHcCQJkM3R&_o@iWr{=$BT^i}sDnKr zCo9c|d+&BA@}(rfNHSO*H1zmGH_Kt&LOFwy_I2`>)*qy=^_O5UCp#F-z9N{NqhF3~ zZXv8~H6+Xm;fk+&)h> zW9(!YVhS8;hG@u(;+c3D_US*1{r}n+O@h(F*`eaJ;*K;BjAUlHi;GJzo(dI{m+1bh zw#sMK)cW93Rz`Yel^T|wTiB(mH!!KD{)}P67mltN6R;<|Ff816(&00j#~d3>9F|ZN zEG}zUH)p}Ejm5#Hu7x1E8*0t*H029%A_Li{MdswDj6f6A<;oTX~^MJj2_c!rY=~zJ+c|D7()cJf*Ii~Ot;jY*4H|={xYChrpgx+T3_rT z_I}%Y@q{G}4K(*^_^jcZJLT-w%W$V^ogO{+LRU|7FS^1gew^wOA78D`rN0Zz(^VDH zp9G5sM4xESBt08MEyJMKm}y~xrRxk2A#{6zlxJmi=#Ul2@)zWF$n20&5=0HjDiavN z2g5#RBwvz&;VjK-Q}xeqMwR^F@YWw5ygG4e=?!C^Qd=e+ z-e*k0p7(lH_vzBIvvSDbK7VMt>-1sAa|pd0>q9Bk?0#J-Lc~vkuLU}B#vF&JDX2Xz251!ehDt*DM!u(2q?Vy8YTjvk?C_+Az zQK|EVDU|Au-+jngiD-xp_vsljjlPv(&BWx)Bc-h*oDLO+8iK}p436wr-tzKNQ)%*{ zCt!ip+|-iJ02gP!vv2 z<4o;iU6EWnvAlc#A(M_hW>R^yxFRrXz=TD^8_pl2wiQPDR$Z{>lEwb~zQOYG*Ug`Q z-8h{#>Ra=5ok7%z>WuuZ-SKPTV|;wKc(&bfnji7;#k4a4eMs%zWJP)ELL-u(G+doS z$~3y*p+(9}LPiqN&0xT8VuWTYnnL0~bvwGanvI4?q+@yn9c*q{1i_(9Ckn*T3#v)A zT~w^I-6!)(PN=TvJftSSB00w&%*re6nlSQ|0myQ{eJoK_ihGN84OMhcO6ZO@G9%+~ z_hIX2!wBu{(k_2AZ{qwdQHV~W&as`SF7ol^@&PB`zo;i%QOs0^MhBy5HWflOArF40 z+e+c4*~tG0n)#tk5y-d1E@s0(;bMACo;t%tghUCXA@+Ub$UC(Fdp8C%hmYeZVAlEd7V5<`(t-4g0_#|BQtWu+m5LSvQ{4way@iH(P-zoF)$ z#Fa_Y`bbuq&WXy0KX#0yc1|1Jhyp4s?9e&6q`Z4>?()0w-0C4s_c+5^aJ9eNTC`69vO0>W!2b$SD=8 z`Dg=#oof3My*SYwu@Dxc8gGTsRJ&2*sd+9A%T?R9Exu`0w~^CN>oMG34i1`qjvI`jKww9N+6#WM3!0 z_GJ=L=Kb@-`N#XIUTC|Zk6O;3i$1FT`!`VEh5S<=wGp420ne+BR(42IC^U@TsjY)C z&?{N7u{rwM%uFFN1DWV+@rJY@>IdfPP66)QDa9<{tCF8n?&ICiZEOU`1gc&4<5;3) zr!L36+JKS6pEk$|qza#HOjE$Bi9%)ZF9d-8ZUbz04lmFpAv7@vFQx z`{OAm7qlJ6ReO%V@p1Uu1qO|6B>Ab_f%hedPSIlgjsbvb#$U`v>>JgS99&jm;C9yy zdd!8`b}r_C+HZmxh|tSdT7TWWTl!lU$y3y<9Sz?$;KMjQa)<6GuzH~TiF;$|!Eu_W z^YM%1_IAgqUhwh7awn&6Z?aBbp;#8}fI>(3R3fk=(Dl`CPQ>g5kKT2m(L4q-ecTk- z-Hbfoe-l!#6OJr+Jg!Est#tG|FN#N*(fJ4h{a0^jjHV(Tg`!Yr-?c)6z4; zm<1M;$=LE#yCD5Rj4jtvK;9pZ4xV&iMlRHm_;_$Iy?qfld4i2O9@tqN6`h%tiQ$8d zAeIOg7UUaV#5fS=@VX2y!ktb|6e9BQk(qJVH%)J&@OpJ6(V3e}3)38a=LLv>Uyme< zA`}l4I@lgMNErTLnH;L)>?sF}3-;y=SG04q3n$a1RYd?;kPq8UJzJhih#*b5MijQAH z@rd|wicR4-(sqt~htn2|O~uclxh21MwS3NL=dyJg^2I6l*%%i}CpgooU0FE|8p0SJ z(FoTRljCFlX$oPQ;`q5}$E9l52DW>PXh8e|UzsoR$#pZr5g)}biZa61eo>pY_Dt^R zI5Zx%sjT|JY0DZK;z65E(OhoGka5v@?=NyM#`YyNwnsvwD8!G<426h<#j@(d<6gp_NibRRaMPw%091}1f{ zA317hQ?y6v`~gGf^`AVr|FCXNgVeT(J)^z5R`(q>pl{zHgKD~Tt#0bqaJaj}=>C;s zx*$y$jo`Vb)DKdxjg|m?1XX#nk0cedw)eMu?yC9`u9$ z-Md?N(<(2T^&Jf;^N#rJSMk}(+>Q65J>UBf%KCdmZn6-Q#ykMkR#C)84E0FUD|K^& zm1aYslxc{Ahz^B9kOcaEMBDPR^Dy$x)M-dV{3eO|Bptu;1<_iaOy zx#N$t&M6aRVoK+vo(Ti{OgW+d7pS+s4*HEA|YGAWDaZ|iKU*!2xBSl5J zXlYo6i2yCx(XgK;ZQ<%#*fIm@p1z>>3H8H{EvYT6uNpS8VeH|rwZ1Pee_P5G$29ew zUR~a$CL9>re{i2c?~WSz4%I!KEOIixhLp6SdLqi6~TxI^{_$xREFF z*bFhFMhwu9T4!j6C~r9AOUnq+Xh%<1oKQvyQWwn1&_hX@s>nShvqug&zW15)@qm}h zTi<`}@N4P^^=Jr=>Qdf)YVRZFzcX^wyMq?@staJS#bd2EkQQ5nqlNfAHr9&SN{>*e zOjAtqE8RQ$Pz&X=t$**>(In@pS>M#RzOJ@7<@1IfN25hVewWMhseJZ6xVKEwcch7) z(Hb=Ocw`N#9fb*`OUPz&=C-fX;Q}-r_-mR-OO1rtqtj?u;G=C7TsGAh*gUyo$C#l5 z^c=FUoSUFiaz(?K;=Y(OsvqeSI8MmKT`Of^7 zYrbC;gng+o_uy$QHA--g+Ujr#V&>#8dRi}UXzJRdXmFS6#vV;ajI22$;q%J*b&J-G zyLhzPR#eiht*g zuupkIarbUV_Zu{;rg>aulkA`78#JK*@WR5LWu?_keVdNRv08tK+3+lUXR#O&9ZIVz zDbnN0M?43fW5C}LLa;pUW?N$POLrV-Mn|Ws9Fk)gI$<*;4Y#%=Z3aX)gPM?0rOp}@^Y}sgR)sTA83x^Qn&bBED#OrGy+$skbi)(WVEB822uA(r zkEm&A#wsXUK&%$fUMAhKICDL;Kvmd=RpkrIFIzuuYLX!^sCAp#()y_^Y#9w3qYj~g zQ!8}4lO<}RRkjs$P3QwucDv(LXZZLM zr;brvoa_)gzJczJ#=eB-+HQd_A>N9J11+jx7Jvo~VHX;!AjUoM0r!|E%Gp3VDN#xzql6RvkJr%UhWK_ zf>W5hnL=auK(~7;t&Rz-fI7>b;e(xlO(}dDQiyJ|F`AH$UIg1yA|8w?h117l;~o_( z`w_M0=yS7NC%MEjS)z|)pOH+^{eV7> z7@E?7zyV9tL7_8HJlgs+{A z2|b#cT>r$Re|>BtLQMa@k<3bV9S3<+k3Bfw%V|d`9pBp%PxXu28~0oKmp<-8US&Ib zuaGDKoZkb-74AIal#hLhETrR3InX?c?RO6D@<#hTWV?A3wfA{0nn!UOR?D79mE8Ns z-fZn3KGE}jUk8f@^3F^ug@<@JDZZ|aSSefz%h_o9R72x^8R0N>51IQ+glgfoXT@hf zbDH9Fo(YxePShqTCS#0wB=Vg^^)dsk4IJx z7&@k@Z&n~Zw|Gp03bwpAA|VlPz-K1oM=3s2x6!DQD0FJ2V}t5_+h}SybzB!^S~{(1 z8ve_Ht&hrqBV|;;bk@Cq3aRO*EKaAAuC>sedkM~WL4|S z_;`E@we=J)cA+&ai00z@AF3m|Ep(dY)Kp&zjn=45NRweHsbx4(W}njfqqOFr_jZbAId7iO%fqDw>3yC#k7qp>2X}jd(-5={Zt&gJ( zr8Q^v+b?i_lIR>QMUi7bfxULrZPKbZ`jg2xjVIAbB8hqm?lg<=)s71zBq_f@5~fUP zYHl7sUJf251Fbtoaa)3Nk{{#hS|>gB8{$DOr@ek0kEavs51+=$Z=d5!=?Zi%@3!M; zqp!u_W~;E##yrrvl{S^pIfx6j|I7{2S;N^|t{%nR@;D7)JTDYBfzMn^cNc^WDE|4q zwgFa}5)-_(h3O`S)=slPbNYSFkl{1Xd65awVxx7y6h1?K4-@n#p4POn_!$^MhWqrA zN3?!Cv(K3bB1pPM9?y{9B2q-hXi*33!^Bh%tqz`PF2y`r4~`E{QhnI&L4FhryRgI- zcBR9Aur(dCmY1exC#%aEuSv?9-1FSkOV5+F_s?i)Rj!3Mw!Vf6k{~fZf*tgC>9W2? z=kr2m{KUs;Zo$Ww>piZzJ#=ANOhm+C_lIo^n27umhMVqOJ8aTBeH}(g7 z!DRhciN8vT@o^*id4bQ0gfqfvn4;430z)j`QN=&eKw&>DHL1mIFMX~7<2=F9-D(=r zOD0r}ZfxpWR@OHnwS;NlHM&dZ?p>=YPM9ozY3ax(3x71v zi$X(R!T}jHWaPvdz6{;O1j7uy6`*T&AbcMCLx@$+e($mcIs zdMpFq_3O09j$fv@&!zT^@)SEBnOP%6XF1)0U#GL@KIdav z`l)TR4{LcT7F8)3Jg9R;3mTH~UAmEN_ilA;n?p0K40JRu3^wotW{SPxq8X}JI1dSP zIuq>O!|ypFCpA*yqrHfzHri3!+1GJd$79C~<`7R*doEQHHs_N1(Rz1)`YG5;MK_Y9SCP%B$bp1-Z}Dy`DA!Z6M_!1QijTg1=%Ianiv~!B7<2NO2yn z`tYTN)2QgMiVf#Ez>7Gy?YyVAx_e)@iPu#c`*B@et1w%~ms4zLA?igkd}Nr_$Fraa@R?Dq)NZHw7n^HI6%<<#T955u>xxDFuy8hEEI#*7Z zFlpk%NfYD=)1tj*_3JaMci(9_Ib*x8TzdY99!pnNj>&C(WazK~bwdUW83uE~j@n+j zZN3`^Xzvq7)$!xhHuLe_;+b~GDYnPQFH&eF;^$MF&BqtF+cQMvq3Di(@$)Ed!RIYe z1^8aWNe8d5=Avgrw3Wsfh}X5P2rIgN1S<%7lLX3>w!6R+(Vyn!B_!lU^1{JDLT*A% zCoBqQwqF#sba0dADf^eLn%TBQTzo(=w_7PrK6rsUdFrVa>iGiY32VnXPjsEh1OE@@ zt=$6hq4V~4+j8$CN88Lm?q5dsp`AQ4{JG zswnOFzpX|88$+D)A6KS}4)pIsFHj%KGHrF3VnOhC9SizO$1>VqyTa{CTRX8#<)SW_ zPCbuZyHX~qMPn2ZCc3Oi$mT>d0Yhu4+%7e<19q>vrzF9LU_!DcTxf>XP+p#&Pq86f z8(dx9y|N;|EWb1oYKsziJs5eqe94&Vr(tF5P_d#zTk=im`(VHLk?%muEv~C8i$#%Q zy5Zlp5$KonSd`nn<@AJuxS#R0ECE~vH7bhE6kB=88t^4lCzv8ZS0vimyH7COIiPT zzdnB$b`T9qJLggA;MMY;_7RuG`}cwF@~l8=a*&yVX0F|xs3hMnPgGv>Lmb18D3UR0Eq zS5#5dwXBmty;x)UU%&(MAsf&Ypc2yg|Wp{UzCj)TiL-b2gRc4f38=uBEAQV`er%& zkhykU8{Z4&T%`T|e>Oq9-d%w9p&Iv3#NM~It46CTm8m)-{{_-b7ssM0r8(tH%y<-C zuZ=!L_r=Zn67ATCq^3rQn*KHYdi5wrU&5Qiu=fp{!(xl62V9+P{8y$p=teQVR$DGO zv|ak`w`YvDk=^}Ym?qxVJ{oURSE;d1n_A=Qg{PrVOZ9O5=3-QEZyvfC3{fP7c4NPo zY6K#?^k+Y|gL=Lspsjh|HNL4nuRN!yW4w)@Hl$=|nbX2Q zHf~f)mil&lVuvn^#>HFu%DkMGMQv?-+@7>J6a%`>eAzyeQRP{PcGkjH~YB{q_5k7-S~r}yUlL>S6yANgLmQoAEx<*>~~8l z4w~lm-A0f6w`r#FLbe`vkZ(`jn(D=5JGMXuC&uOcEd=*U6wgX)}6=UVWRHlW! zy%Gf{v5ppR-{D2BsmRDQ7y9V%fhVFxRIcgq^G)P5IUEj`hs%)Dj=mJV;*VWxxH-jt zm0$T8T_wH#-(}jBQ`a*8zsk52=~#RG6XQs|SAn+e;4&h*z72B?L{f0gQ2g4kSUd+s znk}*rrsQkGc#PX_6JAK)WJAurWcZ>9Cywu0GN5eIG1Ep)7}l-Jtf<=dz-=d9H-2RA zyn@A-thu1F52k3;&z=X{SE1h0)j`9eby?{c;uPT`a4aY$cwj--2o$*-hJAv19Wb`m zMOzJG$to<&&Mqt|EY1#O`%8-~>Q$XuyZ@y^j=MH5>(SEEt8;SH$u})Pv*kV`^+!|QAaP1UsK0$L{d;bYb*RFwpVbEx!x+>5m(7B|@ z@EZk1MI95+*V4+qI^<^TLrz@e8))u29eVOxa9}g|IyHmu9%OHJ&;QUay55T00lU*- z2g3zBn3$QwXRtUyPuYZ#AWgSn;>IvEjZNHt8q22Op;vojWM^XP%2U`5vH9EoZj{%h zulav6$;!IAEB^A2z<3bT`?hVqQ)o$q#tqozJH=UBLJTgxnVE&Hp_b(iMtBnst!iYr>RanWC1cJWHtg&%9gE~G39Z|UyO?+0 zVRkE%gImVV7&l>h)5P)3i5vk=Y$!fySi^|ZJ1ty%(W;dfFIkDY!|VBU&8ruq>WXM* z#4b_&3{(^9iugh=bVFFHPDsG^>$HSaUoy_MeeEyt!luT%N1bh&?Cj zG^8<06w>b6lw`QG96{4MT&pT(Vpf6nmgaO=htX-x&PH2Xm>uxv<2|@q3VWjX(r*iw z-eJqeJ{!c^QvR(@d3>MPYx`!Q{Z_G9X4>pW*PS5_fK*_viQ7!YQwPt9B}-o;hzrls ze9rZO`z8z9?%LAAR&1Iyc9J$@?>Wbxe8M?u%c-p^2TmM0sP#Q6yV|{1s;l7N&NX^^ zBRh%=V+n<^gtOBTV~MuL9&2TJIWURHDAr9q-y|a*bFS3GRX6zo!_kn0#bOuwYDe$FUn|aRE)z=_@;3r`H!QUZu;d7Hei-ICbw*u6t>z zh?M&alx1RMY2{)fXkbuJM_v0Jb@pwbvYN`NqqK*CYYYuSb?2p5bBd5V*q2V!b@brp zVeEk5OL1ONNhq~A5~Q(%NmZ=6x_w2%-9hyJph){$gnU7Jh59xRKXu5kGwP@G^Gu*^ zL#_AL1#8AnXr4OeE^Ht=u?{x~IrX;2X!n*BY$Uq3FeLBjx8|yg&UEV6VE8WGy8vq~ z3D}S-v05UyI{7m<$hGa>OY;+c?>X2bqVFRV zo1ef=bp7pUd@tQQ0N2~5NOaaCu$wi$0n3Ta<8BD3{L~<&rV2s#2*}RFo3P6h|Gc!@ z0-JQ4U0Hm)wlrRlqMHMJeADcvDO1>R+sSOx$!;<-Tn^sfbX31+O`U8(?arWRG*u#s zgmlrJJut{aP|0N)&E#-S8NwB@2n_Q$8{Kfh2pXZVFEbn|B$v}WC9<^N{a@6*33yyr zl`eYrsgg=kX)2X!kV;a`Rgy|`X&#RxYp`s~mhISA#oBik%T~+ z(9lVM9w0yyrVe53K!EN`H_c^eXr^w4CJarQTnMVXf31DasZ$!9-0yzhyYJDR*x7YX z)!u8bz4jXZwU%X10oydnP<)>pxQ5tS;n&ORG<7K?J|Ci z<|!V>S*ua5l_$;RcbDOiAj<+Sg*kZ6W&@fFf7mSJHnO29R!l%BdCEA9ZA?C()k@K2 zTNOOfg8%B^O5QO*2tW58QmH?+}}TTFj%$zI3C&C9>X z8ftWXx=Cr%QhpyG?>dg$)TDLq9U^HUag)**Yt=Pr9i4@I1u9UtQm-b(M5Zq6INa8q z+?48__K%N_@9G)4YV*DOdYX3iXgh1ihEE>uJ2Iv|lWZCc)^~L@G)3IiZ40BrneN4* zXmo5i(i*D@?cLJ7y$xr9=gMd51^rAt$$T!yNr#o=KU3a7+@K7DrcZ#64D2lH0E<9- zGj`UUwnc|eR3Lvh`sVjRMF!p=kPA4yh_jvP+NVJI+isR8cbJgHpy zA;zI`rWA{LfLFr~(RJp`fzzk)^(E^5?@nhwE5@e3yRd+>#B*eP=^rp>%1?Vv&K+lo z=3Yxk?^=jP#IsaFvy%A^P#-0#%VR;|{9IN(D!I~4Q%yBM0x3J0%0{{t-_3nADO5&s ziNhTm`}#sPm9;Jy*6pQnn%uL)9pR2{e|J$4)iyZ-IxH+7XI`rSj*{jRQRF-yqIuH% z!L!!!JE^+}?xqUlGQO<72pl43oVgv~EYKb0Xpzzgr;9REfZ#9Iq^C0CB}RW%x$sJ* z8=x;%FMy2dK?zHmv&>(FZgKa%OSiV1{-QWp+v46@^0p@R;D2WCdh=IZVnpm&m~?e5 z95?AoXGYHVZg{15zQAr;=ld>ki0Mr_d>HGiQX*Oqd6l3v61}h^R9wYSkdew^VHpmA zLAX=0Qdsppd*EOoP*O6Jn4b~1NiM4Kxaz&J!Os2-`fu;Z`|S^i_w#ry?iRvYtB#j| z;pAEooOeb#86slfH-Z*HbLLEb_~C&er(IVvl4|9Ek5CP%>ME#lrPWr`$Cd2u%h%iQ zy7!i+r`p1o?Ax||VO!>a_}j}*zV33hw7GL`Is3@i_RZtj9}(|2;w&jROL4mIBH{8f z%3jLz9N{S)If7Zll~_x(+4LMabXO`;a&_gs4>)d#Qc4oe?S|JMIdO@$ZTRqx?#z8N zmk*viJfiu>dfT=nyE~3gsLvdjpWhp<9ZwI8ZWvfh@9p1nX@fh~67FoP3l20k^4Ux= zEnP}VYsA?^WP)@FwzXtI4cb;+(V;qptPI!*3#w~4`Z4G-bs9bZ#_j)haQ-3u4lw-c zF!Kduh=d+N?J%5E0AWQr^G^bfOpqyIJ%cr5wlj0lM^tNqV z7sS^vH}EKbH{H{s2m@qieh+^lX9~Z0FMiWQ{vs{qph`OU&0^sEY1_e##l;5g>!=DN z{^yRdWe#CxOocL!&1@+NgY8l8o#I(h&Q$c^)Dzk7(e#it^+P$qpiG;}{*0z+nd4b= zj&ByPSjM3P=ySgvKK7j$fsTV8{@HXFo$nwEP-3-Vp%Y5gNQnQ z82c&yWsRhm3zj`zJ3D6i)|FdttY;Zn`Bm8APRjHqB)FX_b^xDMVlReknep*dsvEg+ z7W9kT?QnEmHmHbudV@XOI&PC$50PsRVLHw6&ZVFU~SrzO&T_74Wtd>w6IY* zjE#hqtmZx4FbMNIox6b3^rd-T@Zk^A^ zDL;(ollwe>Uhu=O`24MEnK{2<+o#QW-=|Jk#^DE`_aLLrsfv_Qh9AJB#eVbM`^9e| zqv)RSGfhLvo{vJk#$73kj8cvhD-cFx(rTs#v>JX{99a1j!nX_^4=JIjTUVR~L_1%s zme3ie73jj{bPUgHwP)jgyJivK0`SV zu$%)(4kFt^)nWOvv@vk&MwX&(#1SZ+hbYO}gsdA%ki?`T$sr!C1`J$c(hUT*k%800 z7QIGNMA2ikgHw-KO7x7gcZq~4dsB02_*Ve83|4{Nf1hIdg$hJ zZ@A&^ORsv<(L1i&t&T503+lW_-B17Ecldi*_B7&mpp&WX{QGj8^cp#SK|N?0C!Ix( z-=)I-S>t=&LisnKKYh>Jp;!BqZCaTN4(BRN-i~sQ`WCp!<+_-^kbvf+KoG7MGdis= zaZxLd#b}vSkOZ4cxV12-0UA*|kupG2n$SYX zFB``^D-aZ3&`Qg|DL!uzx$gCeR>3Yp(ju8Am>3hBk`{F~QQ;*}5t5Q0Fo$^D__A_W zO#z2+Ifmdsg8zyy_sK#j*uN4{EO1f-jZU<*Cn(P*FUY9bs%TFjoB9h+MG%$JJad;XD$vfkmD z!J+N*T?cPgpEkk&^?vtf7;?{7dyi zHye(1J#wN--U3g!5Sc*MS9KvyBU~26lt)R%BI109bz_ldfTW=+ibK%Ih%XXK1L7An?omdxL3mCJT%azs2qB*wGBtmvHQ_dZS(igckZTo_PC1Vi9zI`{ISSX1_#~|H<6H zFKqtB+z*NN;Cu3qxtRtHgyaDhL-o0k#94(zK5|K-6Qk;Yxk@n|R12Z_JQbj|CU!T} zqhS$3yKdC_3WEbtLW`2SS%pMY2BovfH{YVFmf2PXTR-3tdQ zORBw-NPD)a!@ZsTwr^w~vXzaL7kuP*i>k-d2_88B`n;?L-~;|U-n)TU{#*P9{<}`G z)SjY(p>B@v;?NQ-{t%v(I_?skSOe3FQIsZ&P;D}qW0(u&=%uGTl(xIV* zwrhHFp#5OU7xo>$>h$C_lWHIo8uWXQ9KQUJ_nw(odfsx!_FE?L#n?;CO?@lsy)`Nq zG~h@TuC)|1YAscR!D_hHlC&fm;R%tImVib^Mv%j?kLJob9hkMM8VNEe__uC#ro0Db z26dy7jPnxLD$tZGvXxsYwu;I#9g^jl+QK8riA(a=+|jgcM*S6MhMw!Lu0DLkx_0;6 zX8k$rLL^uAMYtFJo#3jixTu?{oLIY1)tL(Xd^7NxR4x8fLYt+R!e%9unwo%JLR3UjHx7OXwf*x5rO^U9HH@ZP+1#G#3?n z{-XNN;Kv#zb`i|a(kjePU`@==QV#Qzk*_S`SN6Ud<_BD$V}3x2^)Nrta0FSVsJmvx z{4lFSvqS_z$NX3!KOXg;vyUdDSKZQoa;`Sw5qnDhR2M6J1qH_7m(~~2cH;rB> zeem4&GgAeQnc8jbGnWnSTROUQ^yu?M8>06wL2tsUz^jXDzQ7`Y>k064$bcebkSESJ zT8!$`vf?W*Q}UiTe9-fC5CY1?XYRfHnrq&A{L5o~1EXVI&p)qLY~Oma@n1z{YiE0l z+z;b7%kZ0>T3deS2KuE8T}I@+!bDS#Io3XLpm&7pM|VvAdY|E;5C=u zo7sBq$o*|2SC4gx_-x*g^trxBge$e^Jb3UnV7kXhwAmf5q}GPQ5dbJ!3;uPi-hf)qHY_+QG4`G0BpKq%uJ? zFUdve=3RY#eY_rhjH0n5Ez0OjBPAQEPLtoBlqOKvQ;3Rt1W!tf&%wfP>MHFyKEL;- zs}_3u2Y31o@4Pf~p>^tkw$^>YBCQ~n*t%zC=iX$0N8My>-_GAoY`^JLJE;qh(>!lm z7_n6~n#3%8&U4Q-_$m>d_Rq0h8)xt?!HVLE(w)<2^p&QXLyQSG7 zr00x8$~BQXqLs}ZUxmhb-5xJWVuMFN0lO=tj40E} z^I9Bsi>3EtA3E9eLT4LGswKrylyohei-`iAPoY^7n#guFHAuwKRgq|;6qghhqx+Z+ zivZ)I#PmLRnU^-9Sz<}<#_wPKG9vxh-6MfqK`73D=Y#lqLuAeNp~HYi(^ zf6#!>2ZhO7Wb~#S6-C7(*q-S&)mhRk3Z0I*lqfW}2GukGTC^edN|8ov4f?)19VJIt zJe0|(Q&CazK1Al0HkAvf(@3=qX#L8{m+`z}FD}5=7~itytBYD}YKod)PHmamym4lH zY{9b_ZposFTP!=HIlC^tumjzh@>*_0GD|OoEKlV z+zqJYsoxn7)404FRHyA0i0l&Va0j&M828S!D`8v@AsnkF7f_u*At9YjW)Hyvz}`}q zGhmU>(MT;I5eS8vJe=6nB`!*31X&5{g5RN9`ts2=p8|-pv9YnEv7K(xl$R4ga1-cl zIA0t2#TzXhIxl*|s$a^r?0n|c=t*+lpj)(9(#@3nul$~Lh*k7+0ATQckA5v4qMxN5 zsPdZb(6g3Kq1r`n&o7y+L$l=<&|Z(;|C6wQ%(z8!0cXuZ+ua~C@grhL{+Is7Q~jWfp;bNrp$YqCJs-H}X(008QLDdWmtX}&I8 zxSiOz!3Zwg23)xIxgcM-v}tLwkZ?mEGH%EMq)H*-1N2w1XXbTTmUq*38gJn-dI7y% z*0^G=?<{JefdRXHV0=Iu86wRmg<{o~KCaYV`eJ=tU)xrRd)IF9de6lKl+8EwO9N`L z04ReW|15ua{hcxCkNSi9{rDK^^*j#w4;#@K&)4^-cwbtO2mUtAm%uT?*(q%ELW}`W zUTmL(qf8fL9R__Z(u-{;s|YJWu2xQjQ40mRkEhKv$jC*4Ca@Se(%q z(#Tzv)K!Oez}iFeHB$Y-ytcD{xG1reTp!xcyapQQ?AFKin2xk(=S>NYOIG@iK z^+l5I_^MvChKI*!MJs)=u=oF~y0*Txtt7C}dfTtu^U0)8Bhh0=k0F>yz6#2ZY|z(yheRe`>`mQ`bAz0%C&)6F9FGQmX9dJpnr}ZXC$)Q=Tj6~H)=F^;fID( zHi!Gz0~`K}mX?<8mUJ_YPGb~pHv#|6%d^xu=HJ;fREL-W{!7=)C zcJZW)#=M;y1McR0PfrMEXi9o}j5A={V6OdXu9a)t3!i5BBkzTGYV}=^K%J04xEG3x zph7_cNtHclkwb{cI&2m#N=IaJ-(FpnvWgYz?ga*fub=!n`LC`ghXw}dR*-TCNzo*S zF4oKUwaKLSt=Z=H_KS+9$Xb1W-@m?)!b;gD`Xu;j2ehUH{AFFrE^S9c0BI|>sJ99} z?2h)vLVF?Fo1@ExeGc(e8N)&LU)l^AOiBUvyi(tdOT{q9Kf2AQ~!EidMl309Sy04v7)7Ae@TYV-&+J z&;_Tp7EgUW5)|Po4F~Z(fmn)L_QrHTF#(^@vc|`LQiS0wvf-RYc8GRg>|uyl{n1UO zP{hpF7Bz22N5@ddU|)B8TRZ}J6vI_cy@(V_6_#F6c$brfYj{K@{gUi+PTL`~Q2v`8 z&RBl-slv^}2`$>Vb7|~QLu~Kp*32!Y>-pN$z_DXj?He7vrsSpUC6f&m&%h_GjvG9MA1B!@$3Li& z&l>Rx+-KE@-;q{(n~~Y(E5lueItzA{8g6rDn_nM8S7p$+A>e?`CR z5jIz$Q`#UrY=s4&5kyZwt6n*b__2|L6yXaUq6iu$VV@kh?#&l{!W;;^GN3OeY z=e4i9qzRKFVKP$>HQ~$NscJ^02x$01JMk$|t3ML!EWYuGI_BnUE zzN(IMw=0QCE6+Xx;4A54idZpR`i(Wo^W*9Nkw+Tn^1uU#&){CKA)Fg2A{Eef6_`7e z=!e6^b$S?-Vrcmotx^1DogOPB#mH@&%Iu0 zJr%s3R_gHxmEG17!ohDPkWGP+dJ1!9?f&gc=-(bsR>x2%sy2mUUB&^Ip;S_%jK+>E zcMa2&qO9W9Ei00D7niWy_Ix4~6t?Dd+1*=y_q8B^l_dI_pJRWyT=T=cHnQ12ihhj& z=+rIBR&6sv%-ol~xgIM7ikE`z=-8;tm_;_gyt)=iGS!fFkqBI5Es<1X0^byhH*@um z7$XUAt>oIDv%{N_P(FBS(}XUd6+3X7-!p!AJ{bxRAz4!{vNg~h)@GXO#`_#Cp6dE| zO?BWGgI(HO$;O&Z;Tsotjl-VmV@oGH#pLk&8XBrSiM}Fpf5~Sg*ZP<^hy7>1RIh}r ziz~0w`~o%s^`j3{KY9=KqxaH&0!~WKx)5HF;3=F`9X!Q2BeoyVm>xAQVNt*;K{1py zACS2Y$RR}U`nUvENVcZt_10a+#VgTPItFQ2VvrJn-URV^DBaMa^LettgdpA6RZ&U& zUhNx`{9Zaq2qdA*z0xYaj{Wo*XRn;jUIn&w&9f&PBKj0>EV?4IztU7`2^l4pHpZIk z)1iskJL}WI)a=fyZaH&}e(oNAB>SCe&&c3sKKv1iT@g=x66a+H?wBXa|z2Y>Bkg*Bq~|xxMRXfm55AAR*xxcTUy6Ap<6|4LJuY z>jynW+4GJq?cz!S*N&=;LWaErUa5q9Kd8CLrBjV%0C7A=&6Q@Q$R}SF%P_+k1+F5P zJ|!2JpMWNN*^|Yqy2atu)tH87ypnm~`NYzjbCJ`?!6xYso>^}i*cVT3Y z?w%Tjg1cP5v9a~RGjL9gi|XaAD}7TW|Ndkv!ak%Zq>(I!x>4-|aRAv8~I9z4Eb^ypyg zzVUtir2}2FV{_dj)t6|~y<_SowRWoQrVWGY&S&r5ab{}p>Mf&JZkQZ9l$;K2@|RA# zf^+TT%TJs<;z=ZvkM9CJ$wvENYs5Z;AfNh`Ga69!v1om5X>k#Xz|YkMpq5}a1iC! z5~Z92qU&u?l;wWu1o8UFIO$C*y?r{))0+-~DkRy+Qc>TF_6vIlw{@0G7uU`;5AJI1 zSR71l2xRIe+p?bpaV3rx%QiAGl8XLOhO~J5u%ZFJ1K@X?isPQ`W?5Vxb01)ei**Wn4(p1L)i6 zkt035J`^ZO_xL<{$4H&tYCT7fJ(e*%y*NsbPcOg6JWJ=N=bU59Z?_yL;=_-szXy$w zPo@kgpO-~WLw=aQ1lQ_|)tA}}3iS)Kwgv)%MH||wYouog*=J1haXfI zDJ70#8(py^RM^gI;fDzKWqAnBQvDGCzbub0YIPkQbT4-dbN~SpX^#K{gZYQzjWMdL zXT6>CrH{{5?W4R1cq)2&OfeMG85->T$>IH5B6Xo5MV=oFw2jOb&D3nHNwjqv;>x-Fet(&5cm;v_)EpJZM}8 znTNIe*p)*^91a^&KlM^w<*0W9BES#r^~nkVVoP7JJ-MT2V4*41^R~egQ?n=fFFl&> zs%~xHGSSyNvY~!z{4G_U0hGfUi#2Ptn;)IOc6jRbQ%l#SJi&TTU0mJ2Z};5ch`(pH zch9BznM~Q<0e)*$29+O42^UU?;=&Zpb|+e|c6d~^s8*C9It=}#aIP__7Q#-WXB=xj zQ=mVi+imEVT%@QaMerUM7aN{3+*!t7DRM0H;&P==xcFCp{)>q7Loc$~7@v>e43$dB zMV(1^;yVSeE^6qirzmZMZ38`NtgEG(dg!7}|WEqv4PsoAMxjlI#0?b91)cW(V!_WRb8o)LF_)0TbEvOsc^4qpP7X#DtpfDj zZ}ui$K}S;Q@`dAdQtPzRZQIPRO`7S*_vI%|O5M15Tb{aV>})Y6P_0?Vy+~|fAHD#xgfSk zXN9VmV-EF+uJP%G+AZydkkY7<+~>@7Q7O4|$&@LS{`9YNC8Zy>ZojDu{+RkZD(}=v z%aOo<60g{IBl#?hi88F8w_TUs9AaSEO$wnjE2whbss}-%y{n*EMqY$otPY#L@+oY3 zh1(4nTSJ1bLN3l5*7cIyH5ks@=6qH1JEAvt1)3ZC`RV1o76M3FYu!Kd9); zq3dR^$2icp&>a3wo`ISQkf&NW3TpgFrHR_qLbQhlG?Bc<^qFnV&vdEO4kni_cPyBK zfRvg*&QT*Ix>x4km}J#b{02ljzreXclr@0oW!3-Iq9s%$~%6Smb5cz<6ma0;HCa$AW zwJdtb?%niM6~1H)rlz>=HGKyR4&zIdeeyr}Jdn$57;|}zUjr+4t9g8?Vb<}pJYoKcY5Y!I3{u?7zV2G-!$_m7FVva&jw7YLDzDTn&;91EYimoBq zKKk*34F%a3@;K$@>mxvrQpS2>Fdo3p44JtTR2CZW+w6Pcazo z9v5IUDQRcFC|-b`<3Z#ut#u$5fTLx|iZ`+~b&NGALSZeO5(L0p@i-z^jrI{)>Y~(M*kmpXzU(9Uy+~afR*Eb#XtZH0n{=1 z6jZb7-cli!38z~s!s&sQgN@pd#*Ey0~t!T_zUcR zlWTpL*J`J6wO+@mhM-@Z)qFb6mzu`-V6NMAtbhUSEmWw-E>+ zjYln>;oMV;n#<`-CgM?N$Qg`9`~df&_Gu2>OODFC&?Ym8PX;l?Ll#W$T+%uFdVA7=jzBvBaznw_2+6%TZ~bshB<{LgwjK(1 zO_tMX3!ans+dd$0KLFaAxf4467U>cNNGrzGD$iYdHRQGgppp`RN*HomT&(A?7{KSl z4tN23*GJqMZ?E+tQn8S{GBP~alWuE43$LgNwk>^4VB3qa=$Bs=WBc888P~Qxz&3ko z4MsNQo@UDUN5zvC*$=B*m;LY5>xD`=Nxpk2Cm*9YJ%1161nxRxoaKZ0{e*9j&p&R7 z@w{N0H^-k8kLP}uv5WHgbJn|v@ep$SgW?(eE^6T4jlqZdI;~vZv34|nMU8*3206Qu z=4GVUeC6v_;zcQknN=rlxe+s;M7&950P7g7#IRx@TidE7<4l8%=IJ^ECZtn1*%3k1|SDzm0E&&X$OaZyfVZf zZ8-L^q{YbgXiHsRv?JZ$G2-kW?HTHA+z?L15BKiB=KNJhcdGDP_qi&X64B<_ruN28 zpKq`sv~$zzUEa*Zysi@=r#S#yEv}4eL)66u`L;HQK}s9b!wZxNLq;YA?ctAiyHQCZ z?gj*^fuf_RDbZYnFuhm0e(2x2>nF-pH@HS-I_;AV?+@L$VWKa6YGKpm{WsmXW#aCZ z^hBqxFnQZVZ|T0x{lm_RzUGOoQ)jl{`oP?w<6liin;U0$bha`tR^V>?6y~bhzo_#X z)u%M)UJ6Zz=dL%7kQLb1ojZB-Y3^}z?#vpfuO&l{D=LPN59`A>^6W`E=1L#975?X~6U}`U&f)&e`{r6Zcg!|6N0VPYPMQbRk$qA9Hs;!| zyjQpTI-1dSroRb(V+2K|o(5M5B5JVv=rhM!pXqjA0y)Eqy$BV0q}@x-4#N|LO*0I* z0DxXAA9Oh2b<(dh9K77ai&|;K4gQP9+QP+zxRx#h?o?(e)(a-27-X)LN+A_jArK6T zjIu*P{dU)e$+-cQ$aL=T4Lds`vu(F-9G@=i?cXri-ZU0!Y0M0^?+EPLP0j^zATd2! z6s#<6=_k@{zhq*d#=pNU4(cVN9&cYmLqni-dfHhEGP15Kb@*!N@R~LP`N9=(RkT{5 z%0G6M;`*}F0*e)8A6|nq*y~Z0TEC~xTZdvP?y8uj-9m&QdvvtI%({RmCO9^B?#f%P zSlZbboopR~b@G&Y?eTLj+51OFG&S(>8_(W1(O2VH*yH!zeCMzCZr-s4J+D8B8X?!h z2K6bIXcajAHuM=%6)Jp9qL18Iu%`8JKGX}H^xQtF8qp*y&jzyVybth}bn<|*1ZH`LKTW>aUe-l0n1yI`P0QD1o$>wP2d zNkloRm7|!39a5|Sh5|fka-AV)B>M7T_E|)8ZlMj}JZ?O__5+ArqmF*WA5LPYEdDl^ zxjuC0mS>|+2Jo>w@kVmB1#0`Z%nyyt(@}WYQ{(mdd&$S921_=LjBegCwR{8)Hudd} zfp9p|FadS|r4`7*k3*LDlx8hSfC`_JE*4xc(r5+a=R%V-BQ8(NbCEGNJ&&JfBtA~l zpr7ZW{`8kxOyDIni5TKPOTdJrUw;y}qC5+67bsy>|iT*0*1I_3=d6bhmHY^td-6c0rsVSING`5x3SFb;qNAv!SU zRUvFLvto`kn8Zu4Vick934baOPB11k6?CVn2ZnKddnlAWzCq1?vgA-}Vc9LC&Bx!j zwJ`Csca{~q2qOC9Ia;$O}2 z--G_DDsd7Dc852j^VWDtX7eKBqxqwJD%ElyP2`VatR*uMA z%dtFCDDoBny~_^hL4t+r=XY)QYYk5C6(=UThc?D~+Bpuv>$&!(?8(uMwTXboH+#y{ zQ!^TV+xytrUad-CLzOr@u+{Vey zx%g?MDD59!y87km&A*?V+Bi1TK*0>^5z!=`!F)hltr3NhR8$881|kwBibBNOP~ill zJRh+c#A~L*DOIP{^nxQP8nD=tb3M{zqs3~Dj%GHP%KEo#$KUMx@l*tzQC{7>w;7Z-IMs~Lelwo&(O zDnyAx)v;LWEU}ULL_yK~D4+bTo)vF6(XPztM23=lotn9={W9%CSMFty-h36FdB@^< zZ)zP-?~0v8#iTX7rdP;~OG-$W(n)2B<-P%M#lU>Jq)6hjIwXsABKL{+61A030|r;L ziFFQBhLrm3!h)f6G&ePCb(v3DHn}+l$9vt5E;X=gQ+A84iHHyMw{^5;?-XCM?moC- zz8Uv5&QA(Cw{y^wCsE(U*;=BaoZuX?dx5G5f=ozib`(35V!)cwqsd-yH7Gzm4bdu( z`okdz!b&ZBYp_Rp43MqK-t}I^&ti)T^S&bYBJvTD2Q)b_(U|Ivy5l{uD$YlQ)F5;V z$i-HsVev#@WTw&O1=kk~KgOyVh(&L2Zy)7j$xowdQNwKarKc{91xw4PcJ^#h0Xi6E|nAo%2SDn<}Ev- z(|w`1yQwLj7)aH2xcxKfo}KN&>8`qFyr*}3Y(V-0n69=k?lQ8S$k+T1{E3=wxVwG? zc}2L~s1~ZOC`G_uR=R`?$i)*ItGsbmBMdwyxS@}W5~-BKp|43aM;eBE`-U1Ke|Pxq zyAC{G;i@XH#Q(IR`KGp>?zZT*QStTc72>VS?`)4ZwE&ZXUJra%$@x)T8acl+R09$+ zL5`EpTaKR>U$Tr-Jp(!ZaNc;t9Dh3!<#OLceiHfoW8yHce-!*ivFaG2Mh|OskZ_E0 z3?TubFmzSg)6rl84K><{Fy~w;QA;f+Q@LW!T4`@vUdxz=>z5Z36 z#kq{f5p3&>ceFHPI?;GM8jDf4sFi9zu~rXXTG#^|Nm+`Gpj3Rd64pYGPG5c1k%{2w zikfI#^v&4D6wp19HbU3)2PAb6mZME=bJ=_7-sEzBrH}Ib68~s*p@F;7B6O zed5q%{g-VRI?&n`n{JvIAJJy!=QF2H`Nexq9oCKxHnntzg4j)5p8^;X*$-`dT@B^Sntw&c}8Tas%EBVwgp4oElmSQ$B!&| z?b(0AkKxM=`uLSPtp>?HrMO}VD`*lVj=}`q6sW=n2y!Ej9BCWz(0^J84Iq0`#3bG`>PW-;LiSLQtINLKY)ZIK3NjL8v9^IeL%+_SS=q?}a zNsreC`V;Z=o}Pt_tMF|WJ_y0)F7|qyFjatal&VcF!Q^ms73dMV$0NrPeuOj7XR`n} zPV*t1G9~dO=`${lWN;P)l3sBCtvTHvoH~`+uwfOqh_>C+_uoJLrd1qc*}IUE(TeNq zT!qNcG3Fe|O~;~`NwQI?ctaV>J*IOst6~6OHqy*FFB7U55LZ6S0#@wtXK6P#Y`|H| z+hq0eZQ$~%syERBc3bxv_XXr7Fe?GZ_BBUMrOV;%sT#4nWh2TWyGh%awHnU1#_QA)Hla}J8RdRCY zc;QiwI-nGw_`8AfrE&USCA(^VC9M>vXKfDW#ZO{Nk<^5!2a@BG)fk_vr14pykS20Dqyp zN%5|QOsc5~3TX%QX{CPod+sU#OJtk_ z2@hoR!-GccdW4V#TbHCuwe=|?UhT?iVH_>JvZ=tAf@2RrL_@w0q8%Y5bw<}(-2>zeXD zOH-8Vupx?}Q(*ekc-{5ZI=zIfs8%13T;`!#8l+ba=0S^4ITsA$}E6pn{ zN@5+_K3(7IJtd9Nq=N9D8x{>2qeQPYEt)(_hIiXEW8|(Ic4~7>r(~Y_LR?@bZ9c%f zz%<30>y;x~MKz)*9;`b+JYl#Q>)>W&L58*F+>AOuQYT9z=(6fVnHq!U3RqdxEu%wu zjIeq+I`ghtMvjYD%zrQT>_1@#@Y!(zcEm;ioEGo}Q2|sC-pK#Q9#<(480?c4uB7yG z8ciPhT;e}*W@dGER4bP2oWgy{u1(_R>|5ysi5uwbAg|cg4&I3>`?S((WZ~oYaCVIB z1Roz+GPGgN5txnW#F*|NSO(Lf6Akl(jXm5O^a$pKC(sU|IvztHgLO~cpVp)6c>3vv zC(1Cwl4)D}p!2JjNIxIetdK`HS(NVm!@0Jvn|}AP?OfCwoYaKdg^`6+fM|X-c&CQBjA#tMoaZ z{{_CM9sVfe`FDyf_|&eCpTXQIqdf?ZkrO&BHiuXOf{k1iHMALGA8_^1uE5Q_ApkPl zp=kSf6c3;pIYp(gcK!tB4Ru2bB9*3+bJpz@_VPvwy$Pho6iQQC4;`{u&40InR>HSN z)2+cw#x`>{djIPFO${LIdV53TuMAlBEUNDr@H4b`_&kV}Rao}paUo-mU7*gNI6f}v z`=kKmz}P2_kIV6Ma-Z-!E#f~+-Y|YAD*l_^BVIwH3i>_#TX@tTnEUWS0ptXHw7+@9 z&+~L5aI)uP4W4p1F-;v2PYCp25#k7)OOP1%Oa^q`Pdnhn2fD2TiDI)Kq(`Wi5X_<) zUv*VkDT-&8;COgaRTi>4(ysh)d2iqQ_0;LsOs=C)U%#!?NSR(<{;kD}l@{fz%ojM}EHiKtVaL$;CqOr+Ql(X( zY%|nIcwv>zNLlct`jGwZNFsN_mHtF*YBZ2+_GCgE_w?0GRz=79#DCN8f#14VF39ga zFOc47j+0!F;}6rgV&gdA1k*BzdLpN_vie$t%_Bj|$TnQVF-U;|;Lbs~9BD}3$&>6O8#Y{rxZM;XrdAmMbg~!2${q9+Tlf@MjU|)C@Yim7rGVD5MKc;DQ5pU5Znb z;Wh~Y?ihrM;s)7`ofpjqb@J23fBO^W7Cbxso8N$}`uE=mJs^O4P1gfZuVIdVNwTaT z)Y0_-kQ&$OB7R;bL3%+z4N8CS}m19(z)edV#<&~OcUnGKVX3FF~pBR5$H6q z-hYKj6)PbM6yWxtdqACKsiRN3s+!wP;acl+qy2jyU2a!=>&mZx?lP(<(x3f~2>n|A z4u2o(oqqzUNbmet)+zI9iJ-ST{boeq8O-Go)cn`yB9^glG#7N_p)R;oh@Vswx{YM9 zbS}hC_p8sC$VoYoir-~_BD{aFtf?F@fW2A;RaqwANwo_BUPDv!QQZZ0Sm1IaRz_S? zZjc9;HIPgK;XKPYYo2I2gL)N8S=3GGP_Vs8Q#TyuJRQfs#WIFM_7c(Oa})(xexIRA zE@wcgpJR>`w|h6%S|I6;RZ>?MAR;HMBP!s)`4+ELv}ZCu7m4f- ze$A>E@g(}9K_>qS0+;qM+7P5l~Sq=S~Dj z%Aw4M&(WqPZAo`JFJTtr;zV~(a#ymiD{$B16CGuq@v((qPx(S>ym!hQsT5nDSZa&( z9Dnz@EUMrCR7;M9c2pP7RMtt{i;e29mH)Q7JiEA_*&IS-_NJTW@q;O z)6BaCpusGDyAE#MNlhz9-;|(RwK)Lyu_06mZwab~(4kTsHZ)LyNeFEi05Z~_47D}_ zg}XZD4pX2tP8K?48gfL2b7?Uzhn!#{EmLa6Y`8H!v|(0jE%9XPI-IE%n(B`xODd|p zg_z;P>QGl(pN+C)=S4}s)Alw@{r7up9uLa|;+ai!4)wkLC*?2l_ZItKiu~9;#xppp z4~gsLyXXf^{KoI{87^fy>_zPa%F*yZ%K~<%9<)~}1r+ho@-id49ZLre1}BV^e`EEO zhT;u+(cB3ySF`S7O~+(*I?8?+mb>PG0v)d%t)mq zc}QzNY0Mvd{g}8T``3CMn7^hvF#OCw-WuN}pkDD=OQ|>D-DYLCwzE;#kYlepPzzWk zJD4y5Kp)3r$UUU?EJ*laU_jxbR7muSeuv4t;iXH6K(o^1i6TKQjtV5`Z%;~FzW+Fi z-sxfUz=AhieVq3vvIH5JSW2d&Gp*Be9Af{B$*~#net-v#Eoq1Qnw!$Wy18izuV>%O z$9tp?NB!TXaS@QMmJ%nhrZS~Q^OiZF^_z}uOfV59$F@u`Be6li4RL}%B^d&f5SqnS z=ls*xfn#C1wTPGLt|7isNFQ#;pQI1Nn*u%Pm!SPU_^mJ@6M_x3K3taWs&b%A2gs~vs0Ny7rvplP5Q$_PI%IQ9$6*9>WV|5y{qvcv zdk*ZbXlosr@ONzQ+PgIw9}34iQp4LfzV&?RsUvF0x%|%?Mw(NehCsPH)stwNNDL49 zJ&9mLysBnX|Mp!(AH@4!6#ba5hkRtXPH>A#wbu!GES!*2hOP%&8gJmZMd?nEU>ryt zSZg2~$`}wye?w||dMF*;mDv}*rJ_n>;rE5$P_H=Z%s$sQy!nqnY0&RdZ_exacR!#x zQOy)xU63zNt}@E83X=u1$agBsJnS-(mq0(6sVx%%DUXT@NazpJC2jqf@}pkE_h`Y~ zSI}>fdci+s@D7E@i_uRp8I~Ot!z^uyvx!#(H5n3N_44dHf(>bDstDK1Vk!GmQ3r#? zxFE&p`RVL=d^epRC@`<6WKH9n>wHxu(3Q!SqZxZihe13Mr;3baAO&<)qn3?l`m_+A zBD-wK{7||E(d!NvmI(gO;u@qt7AYS3pJ^X!AQY+p(0pbt*&EEvHTE`SwiXRt+BsDb z+>}-WyXUg-3LR_hKurFMV{Pr(JJpIUi(Rd4oh0XY?l$-*RN6#A`Q3~UAISG49J6ezgyii_YYfT6Y;PN79v9@1kDJf>1mf7MgS z$H9znl$Z+9SiA&R<0_Be#Ry{h{MIE*&g-`(eVDIP>K~!21(hDHs{!3H>ExpwD+~)q zA^r~QoG-Erv9vr=fVt7G5d;;r76^k=j)XdT1Bljwqr>7I$YoOeK4%ZQU*J(i0+2uZBH!=p}(OE*UCrCsE3QfIY!iD z&2Wx!l1+hbq12k-Gvi>3S3bnCDq|fKuc;C?#RZpQZXHz5%vc8rM$A;rV0G;M($|p- z+JFJ_9x>Me1%8d_jdelBI^=;eSF>6&E$Co6b(<7W^4@)n_hUcup-Q27)&q5H6kG^c zcp4MO{B+9_`g?#iB3*#yxY_aqZV=+%cv!+^$@t7gOAW@=@$HY1;%_1UEJMr52oO)^ zab|W31_5YAJo!qC&C50c@g!^l_7}q@upvW}?gM>~+^CLTiVJ}p7|}u@C_?y z3qg)4UIlz3)PQUR-ZUrd05q!1_1o3&|CWq1dlw1;jwhA9AXFmg(#B=Cc7G zuYQ(3Ey&SD`Pk?fiM^nHf-PDT-T&y44YAIGLeT?NEPc^Pu&R>eE}&B60ntr6HUbb# zr(Bz;c<^-p$&K7l@xmQ<+^z<=c_RO7?)fi2|7E5j|0ds0Da?WJeV??9)BPmJf2!P< z8^;{g0**JyIa9nzf9{OsP#+z+2|JbjDl#^OT z3DQcyi`X=5k)p?tC>8}^Eff%=jz3P7p@O3@1JUu?bv|Cy+%+{$XHBRE9n776r?1gR zRs1QCLNghpY(}*Q~e}S6ObgiH+uXRAJpj>ob zE9G3%S}EuHHeR!b_WS?Nn%!70K=#>iN`-s4MVR z&5h;^!c~NAQfg}eAu&^h2MrBs1^e0NHqB-maDKO)bO>hQyI zhtr2)Flxb-##?lMuJHl_G)+x`KvPFkduuAt7)Ul&d!tbHYf?hT?y)h?DvKw2Q3eqA zCqw>f?pKtioat-VB8;t>yUUr)=GxX|3xh@#dR}xpa?T&q+^GMi0*{WQmoU^SJ1hX5 zMy+GE@LV-t~gvcKBg?uOj+`%_=sg3F=}1Lyi@!HFBIsq$LJ;01mx}mk`YlZ z3n53qLl2Q6Y};2_;DnvC56$=p8(s$f0xJ^1!Hta!%whYSlfhv=E?bY3fFhh;HM@WuK5N2 zp=Lit`BKU^HPvJE12X=6ZUsqAdXkPOW%gsboRTtI_WG%ZQhHue4G+PsMpVO{iHeZ8 zu81*-8%v~SW<9-4=F@mkdf?W1c2SEoG)QVS#2ZlUpt8QQ&gY@PzfPypHR!Z9ZQdhX zSL)H|Q~5Merlt}`K>FFWXaU)cd(NN*%WRWIWY0+&Jubdr;kkKp{7La4@SMEU=hY*? ze1(+`jcXYTs`hC0@{!G8-0rotwc*-OOI&Iha7bHk_AVcJ`;cehfxC}$9o6~Hwvchl zA0NRzHtT-R?HBG4Kj|;hGDt1vl}(_}ZLq2$+?j2a7r%|q!aVMM8#M7aRkcC%01k*i zE&g4w-sF%|iw$Xsr^p3KwK}}7I>9g1J=gT%wdbgw?Ywr&Z(ent1FAv4R^>_!S{%)j zDY)sWC~lE3xKDSN5^zouk`qoDA;#E-288h8>a`zO>siA7R7WDUwUO2cszB)Ur;JnB zb+1ByOIAaAy}h6+@)Y;K=$_nRfB~>0bl(2Y_J+#Ky!zgVJHOHN-22wsoLyV4{mR8Q z$C&3l{4K5M0Jc}#O&+1L5~Rqg&B&(n0=;KPCsZLeDhPfe$@*$)MhG<==RwEsNpLQQ zwc+CCx`jP$-CZ3q>azrO+Y3bQ+8tzHeQ*Al*DzM}uDwuOrFmoP?P0%hu~kD+(f6ki zS7bMEyPwrOxvC~zoycviM?DithNe8S6iSIoo@C4oGb+3^W(5mUUStUis5nxtHsQ+W zMzPjc*M31ul2o-pD}whyzO?pNliQ}JM^So)dxi%3Qcbalyx^e^U977hQZrxW0DNby z>s|JI6-#SVT>Z?7JD#rnf4vydO}U%9>)+3QW0J2a^%IcO0VPUR<7+?#Tt0}Y{a#Py^nqjVF?P+)bly)vD8`z&*J&6OT3o`wXEw(>ttP z3yG@+w$Cp=zo1Am6GbC>$l8xruT7>!1?~Zd;TZyn-`uGRgS0Zhq3fqm~I0r1k zdSyzRfb2v3o$6_D0Adl48p=4M-WX6(^n7%{q`Y2~o3nfCy>+!H3|mxPQD%3bC_Xvq z$lS3RO2#T%M?Cf||ChciT>kvqGPSky^6YP?rsh}Nlx)XH{ow!Qc|Pd>RG=f@ZT=@f43JL|Y2zj0KD!jBZ_;ec>Sz6f_jcftusJ~NkGHUP#IPnX19nrvovH8h;=}bgnn-0yh|i|=uhGl<0Kzq>(6%3FbYw8 z4%NeGK`~b)<=jxOM=BSLaGOIi_BzY@YwGTJ+j?tLADvnjMuWQ5>!X~l7sX$JkGS?f zxT%KtuGsxjc>AoGwu1rH5_h0k6>f4kXxHue0it>X2z>=n;X?(}Bw27XxD zZ&(NWp0;9OrSFOSV^YWdH2L#&ACdHzG@wd-xAH};Do~5%p=bmw3NGJAP-D7wVW7#N z{vk+6y$Z=9n&psjLNz78Y8K-}=3O)^$Oi@@)Mg?A4z2r!)|j))^_SN9a@@Rit*ybJ z-QL>Wn(k~5wggj&n7zSXACEUueQ=V;>bfTO+GdUTGf3&C_3i84eCgdz()(L$Sl5!J zn6Gq~V2;jR;513+pJ=6^v)u+dOV=<_7|MihGqd#vt~&(dPgm;}xt6H|dF(4qXGv;c zm5rC}c*!9%G*}HhwC<}~XYMT%!=b-TrBm1VIS@Y~iH~jrE6DHn!rqp4ce!Ct{%>x> zqr!FQwQhp?!qn8;U&|KAd#YIdF{t0D?0LGOjC5z+9aIY$NCjnYeWsUR3s z(PvJ7#QGtE3FtL}9;=P+NUaeR$@Rt}IoTu2F!7%#=mAR;J|DWso}1`DF*AL#e`2Ji zHFH_IJJXShkH2YR@=fBl!y8AAj}9LnonG>Jx1^tb`o<02Pe0wY*_VBCZtL5&Y~Oa~4tii}b$(ulR1jEQju^_&0`-{%(jKx2I8%<6 z>vESo)SMPWGKr8VMju)bio<_y?#@?RSyNqw-zcvJ4oR2ISf$4Sh$oI_kH#9=su``KyOxYT&*%-tx%sAD!w?W*Sr3*q5Jgi)CL4hM`CE{8{D_ zu7_mqvn-3{IQf3$_+#RB-n%xecN+ONE@bC6D5x9MQ17Xxidl1z2I?$s9A$=}`I$`= zI7VFE4WK3^{Ko(+bV#gW9~2_UF+19KAXNZ+l1RGL=^Kw_PQDKRy8OEArm?Z{5wTqx zec(dj*PP#WT)6Ir3kBaS`Jv;E>-HQh5H~pv?D?Ay?mSeGMabF9D z03%@^YMm3>OOMb>b1H1u{7^IqK}}8qX$&alrclW$1`n?D1RMl`0P4QS1F>K|Sx&J; zULi1SjEULBq4BJ}M+S(uIaa@=Wz}BUT8wsn_cS);_Do;z!@xi_0JZW%$zVZH8&W}4 z5^+xvDq2(m?qi>;1tm5Dnko>u)}OGg_k@{I3yIIY5kVfKD7XaS8@7^emLe>jdy<1> zdaVm-!<$cQmQwobKL*L{;VNHEL3l#3wEAduGOv`r(ZS2)`Yu_pAjkXalB{3m^~xhq zRKKk);n_BGt#J%woq~I!$A+ zK&*^lM;2jA8PT~e0OG$gM#YGonnPwnk4&Oq1Sj)V>h=F;hO%wBQrmKkD%E75z7IIh zU~QPWD*8T9|AG>nX+F;swk7i?Omc}&F&}7^%5CQx_h5b;a!3j)i)Y$MR`cID&$&Jh z`g!I`a!7~BUnQYg@)oLIDLKzv?v5d&KnAZ@GTxuQ?i9wNr*2u%$3b8B7VIsp`yP3& zE2|Lkq^1pp1+{RY*#P*VA*9LYh790|d0c7rM)iG?ah!0;5qj!_fE_k#sIC1R@(^U( zha?)6#sL+l;#Le5&)aH1%m~StuT*5BO~ZVkiRiyscU6Un&jJEc3@lP-tka>6k&5)y$@{Du2Y zC0H%;rm0U4{w^C&J(p=)9F33FWokDzq3u-HzLDfqjbp-5zqu*<`GLBc{z&&2I#1m& z)}Nl}sqPD^fd_6~I5Qn-%D&_jfn-WHpK44_(tVB2Q@=|!9Y1pPs7mdpw!fHwn-4b> zVQOv!ttynGl3Pkk9k{g+Acv}it^`!l&nAb=PMH7uAO~n20KOS@BU#U&%or?R9Q3>V z-p*lHW^1u$wnGgR?k$|%oc(9D;_{(k(k*CytXmwSGYZ{;fUGL?ix*WV=G~~Il|ikq z0oJlD6|47BOBdK}&;#i}6FLuOSL$}bDo^mnHRYQo3LT&1NIB@N-`=5F8AOigF=V`Y`L zs-k+d$SyDME-Y{bp6ecrq`JGCgF}7K)m9Z0cGq?Ey#4KcZP~4%U_rdLG!$Z;X%_2! zkTDK&z30V;d3*u5Guo$!D#ssDe#+y>2nBt}hbhM&6=!%Hxuto}b51GtdoP|}QtJWp zKu#nWozzLti)gAh?0_x_)CvZG1gVNqPNZ4%hqH)8moBFkrqy~b=$XAi+{z_A@%@0& z2(R(|NY{nu6pni7!XTZF{Yp^jmCoz&V2n z;#J&RM1vaTX348AugeMN8uVh)m7%YIVO{t`wT#=0{;dSflo>_@gC&<7=D-q-5J`Mz z>2lxtsRVAK!D$pp<^ob`58t{71yntUSLkH)A?Gvi)N21nzLYN z$=nYRxv?IqclCAPb!!N(>nepy*+vvYg=Pq8DP>7P!0eEPkrTlYcQhU*cbi3^!u?}~ z>WZ%u^vN^bX3WA&e|Sf4A5TKGErDh=@DhW}l00ioq3`U_bC;k&M!4oyui%FRZ(7y^iUOvEIcpD%%E}`a!{XFDR@1H(WeaL61Wy5YZwg}C9G>R z+F&B;YRGxG)AcMCeSdM`l81&bWf9wzOvc7zt=*m4p4~U?zHw{k5mF4%Jr`mK( zp9-cA={t2Th?2I}sP;k>d}k~oSB*m%FhP~$%AxQ^$uza8$MGiq-&)LGPY6cjl>1ZD zQ-Xm%>pcd4dAud+s*W|1znn~T<0h2{h%_4RHwpn|CAPI?GHr=?>hPu&-CB-K=h45{ zx;yjRirVseC+~5Lr`y|7 zt#$Q*+MdQ!nG*+5+;F_HsiU^K*6oY$SWHa{=T^#ho%5qO)*P$ukbY;?$u_4AP!^KV{`EuXWF=>?JPEGDkk9vE0SQg?eHbxCeIk3OC)VoQq(tT}b zOLMd?3Zb}ZplKrB&@fWtGel<3Y!u?A0ea6zz)SrUU2lMZ3Bn^{>nuhxdp|N$(IE;q zk2Jxt4vAzSC0@}rwsF zx^g$b;2tC@*VlPNwGDM2YY3zgJ@di-n%ZQ-x;)E`&^;To)uzG$CQUgM{f$4E6o#B_t{hGdUbAXMpMK%BpJl>``- z2VV``e_P&}&qm~FCq_|WH@0-w_B86ScCL9H-O%|+)R3z z_|3I=Q9Bb=mSsxfiSCKXO_@yQOv!cik#O7}+ptk|Z`La#+F-pv zN*{Ow)at5dVjrb`ITErODP?oP)dPI%f-t3uC>pRWXFO2KJWK??~j*; zxZc`2XYIAuUVF`J)hY?hzI{44pB>oB@N6BpfdImtDsk{%;9(a5CFsAb+@>s*^~IJ_ ziZ$&e=BTd$PZ4FAa7fPODZ_@A1Ky=|UIUr~bGdS^8wKm853KD}6U-0_@tI|@FPjJT zn$MvOk*-)x>DH|l82welB$p_Rjbz%my&rfX8!6gG?@%JykrloHv3??r18U*YCb zT&rwlW;u+t3oe(A(k4-LY+Xb!EjpUT;N7xKyLLY^L)H>Pm+OjE0Kxu*0aD zL3|)Ot`Z4%=n_Z%z zroXwUF5BbvxeF_6Z8Z(a(-Ej`?WpzFHD=lxbDS=hYm;qwY3ga|Dm7L!eR{^kWim3=x zH!E8|c{T$2%3u~Gk+iY`Acer}(X#^)`>6M*o3T@p#I(xKIu+oMN7NRie1re$ew0Mw zn71vsegcU7(aM3BdVf?_trvXOyTXjwGFPk;jYJFNO_D+2kD6*ty#Z5(9s=w7HonjYIKSzGPM}?TPz&xjTP&l zZt$VgO)fdjWGI^pV6DOz3dn13MPDb3V_sL0kMF;zyG!1GSP`{?35G}b8f2PTgZ@0p zvwOclE)>>EaX?>!9$rcm57CvpknqG&3Lt!t2DHK=WsF@dM{G6~sx*zXeqh_Ll> zc3EDQ3+`vw6jR;L0ee|9G@1x3^w1J&eYK^lQ4hGSzi)-u9-Ek)5{E16 zy6=tuuA#l95p_a=fq-TY2$DU(5B&JDP^*bM9SyTsrP%{8mknqv3{b$EF=ymqHrxUzF;zdNhY zUl^`xXz2?KMtX+waw-Depv&C`qYBY2)hfXrD+AG9Kkl4NK!$W*y)JRS8#hkM6(idw zW6r#yE7~m^;A%UN0ogExTc@ds)kFR8B_%Mu&{mHtgfNTZ7nC`m>FK#rnX#q)>Bq=pf5a-u0AGkG8*(b?zdTwDbyku362L7u=# zH}G)D$tMZ9v^NeG2&99yG;z5=5+x~AY9xzPQc;J7K7UdC z4Q$VD>-3u4u?>qad zU&H;##tN`UDqMlzCrNaqs0x_koU*P+0AUsAqQ=RmS_T`6U2aK;@qwmpNcFUPq8-;B zakcm}{N4=2a*V*x1^Hvj?{B?epU>IfHcB|3svpnnyYM@<%UeUJSED@T7r#ZjrEE73E-^$)-mR=T5| zxG&PYcBHVWVH7$bPD*tMNcjS4pgbFml1Z@aKun7$9A#U%fmUI{&dyr%BJgQ-GSq(% za8zz~KBNiT`=?QPb^)-O5uR&gh(Cg-I+VUg&~TmdQRJC`kyZ1kD_pk&8;6WyZIF$u zKsUwdQelj5qcT^3D+VUI$?MfDv7dijVfK!<_KnBFRbwr&)%7)N$8gEs&|EVm&LKUB zYEAu-ZI_=14#;--ZTv*7OXxoRfGrQ4f5#(UqiTh9T|(zYSqNh~0(MYGW`-Q4z^m0N zD|K-0vwx8Za4^vg3$!G$EdD)S)jj>!;7|XpSXpvilus-UYWURa*Dg`M7KLw05!GJE<9swJvXpFKrSKNup0XO! z5p73e9UJ|T#1W$dM73*KL~SCTMkkmKWxDyurE#?TvAh2 z5f1s0ADT=YLLjZk^F-=bPa1EqRoR8w5U$mj8?EZndl1=v{=$&Bo=Q(`l>`71j$oP#x%~vj6rkY zOiYNtn9GmE)6J^bN_r6}eSnPC~&GpE>tSAo!N=uwxC*u0e4zs<&oyy>~UOO)?Ff}@MdWE$@SNx`*eP{a&zW5ZNt8e+VVu4;ut0K}4> zZi-QC%tj>lN*aXf_HkdpRQi@VhQrJqs9|~1=JTWR77H?hwZ|`*4O$G!{MN;e^um~A z&3eJwOv?XXgdT4>IT3gPDK3@>ya*lg{nEyFoX$UQquM!(+&f$o_Y^&LgS!6LMbkb$&xU%d*YJ6ZwospMtu+-W){H`=Ahm z*(a(X6jC@LXb^#Fe95Ld1q!42{1h?0U=rQL=@1~w0|6?oGC!FIF)^BLPYc-s?-#c> z>Zv>(U8WS#B9X~c(_b(+Pt2Hm2eKLbaB3Fhi4w|Qb|Iq#+9RwwNZlc34zTq?Jh;sO zDvHb5^TKu)mEA!lBiO&8h`ZQHBL(a-{K^f`Oo3nRY1=qdOWWE>F^4Zvw}UP>ywiCiT5Er1KuadLVfz)fAzd z>7A%MnJt1l3YGH`DI;{T<0g^%=L||vXi7Te*DZ3tb7JSmOgQrjf^9ruvV49a; zviOx>2xx$0_|LW`zO(pDZp>#iCa*OuvxM?GKR&Z$5e6b|{`j<#Yvv0Hr%WrEyKe!5 zM$7_`Oa0(1<+!{{Q?tf(4Raefa+3_Ck4=-iz_n|Wt zD)3?3VZNZyA-znI8&oL0jUY(!LXjxOHA4`zEqhYx>O;z>GF%AZmw}L<>%37>O{pKq zz7Lvq#lD=UcEqOI$7Qk4aAQZ;?r6r;T5($ZOQ&x-?N+XQ+S&1i=})y|nY325T$=!>KcZD$<6)<2p*`-Vu+-dk9XXHzId78*yO1s22rw z0UQ@dA{|E4JJDy)q3I}i25*kj8Bx10h)th&er$Tz&aGQEi0{UG0bITKAM_v2BR`jU z`@aGn(oN?0oXT;M)#dmx%I`|dCz)K1A6Ib}6XU+b_?J~P9XfixAszV5BR?1t-qhR- z%G=a1OlB>DUPg{VAa^(eK?m#3Kn49xK{M852;}lY#xt601HiS@;YgdM^(EGH!_o+l z?)0GXB$f&g7Q-FYDUz>c+cxU%C2%KSOK-y8Yf@2%SM-YSVn-Nt@p^i8YL1CV>4~Q2 zyD4hRq-ue%WSAmiM_eV^Gf>QyM&bzM!p3i-s{x@-sGo|ctpX$T1L=-5BOgP?!BK8c zIN&$iTf{eKdk#+xpsr_NO?&pn`r0);OVz@y>sB0|s0}@ZAT9*V$Im~szO`*-{OM9! zcN+eA1vFz*P?y^VrxoXdNV80WW*iz#mze|3vIycJ%;J^(7a!gAuXmlcZ~v}4fAbsE zRDb$uG#0>Ff!&SxLY9KM`V{p-)!CJS`Me>Ycmw+F%F6YUMKqM`-)U{^4CRzOID73? z(}vhy+hDrCsRk%*6wfpL@Uxi?p9YSI4zEcWCpwhl$F!PPaz4?a96v4|NzMn1`uJ_) z-;?7h&;PPGhWGHA)MC(QzJe-VlpmUnT7sa?d@O8n9*|2sz)fZOX>qCaiA6{BRqoDd zstft@vtiCNf4b=K?rRh4y_VSmD715Czbyqel7Hk{?ndkqohgZLl2hdPH@HVN)j7XK zd=>oFryw&Letu&y+oT}V4uB*`WFmQ(BZ|N} zrJ1;euYGAJLIiQq{)X1Bf$!X#dCk#C?KV?&YwzxguF~rASUYl}`|Dtp>Wr+vJls>c zp`+YCSXr~emDd927TGnbSdMZdTE0HIgfd;kFuV!_h#OWQY7>OBno)zRUb2Y-Z4YwN z;qnv^I#4rKqaTPQpvnlyGT;`ER&}42Af?lKYHDYzo~fKZe#^Kxx;*|}ovK9la#$(; zJ)XoN%Z?I-oqZ5N<&5ya;Hw{>u zK+B~}%c`Sys>RZu9$4B4*6@715j5q&vp)|SOs802>;qb7g7LBWd=fi?dk+&Romy7- z=M*d~reV-DpAp0cCB`WRj>ZuK z2Qo;GlO0CRzeLzaOuHR!;yKidN;fNCV@c^0sgfsh?K<%c5uDm5U7~+2>eG4?NO{vd>*uX=Bi6h zkBe=G*IvGVY^?A0{$u0)xAuQQ+kozsy-Xt{BF3uaz)sYX~W~N3%^G#ZTcC{l^5os=IEUho2mh)@ZPEPn+O1Y(!$+5Ie ztxC59%k|dteXC(Ox6NIG^8Vm`Ex<(s?t8ZqP%avIARkN0ML2*cEPxvWR}*!JA)%&_ zht-9{h+CzvBy%2$lQ|DwS~dkvdZ>CVdp_oW3MD2#KR=M~FLQ>=O(x{sa`dz`03dQT(1xvIy{mPzq(1^aDmev?4kn zvR{=CN^is&u?Ivf{`0Ayh)tVb5YzEDMef}5cod$s5k0)O;aQndt|7?_Cl(Y5MT9(X zAX2~6XE3?-qoz68#5Q&;)BR9Lg?#UdPspnQ@R08MI-cpmw+EwrC={W(?dWTQpi7(< zOaQ|+Qy2{79FtLy#NdHl2Ju43ih>LXhV3mbprA_^xpjSZD=^+ zWld0axRSx^w)l5M|6sH>HzT$zYU>=1PTMftK&x`D3IfjGZoK#&d zXk)6f?ZjQ1w#^@E7E{V-Ruq+*x8IA3gMZI#fE;@(Ru66JA1pk2KukOQf=CDSL;yT zN20DSQeX8X!%&5KFO7hpR6(SP14sb_WeE_dLSZ|k$jOTq^X{QoCNSxB`yFDEKtss-g~>-k&^Fam-$}!p{9&;z86Iepj-_WgfI|r z_S9@I6iXoYD&lCMK#^jf`mwa%AQ?XpJUQKq!)Y{I3Q9~1-54Ql1UToajd@5z1<}*3CGDO}`!a~z%bd)ol#<;uk2o3IJ)0Lde3Aj* z6nOnmS9>J(?Z(LJk)gGb#&5@(TBCKik7D%Se`<8j2JfJf}~4=PB`e#VzF2)RyyHyC4>Ud!=Y-F;Rf0D z%gW799DIBxx_&)K4GiT!geU$-JRUq9*%0DgLl5_t)B%nbs{kJ-Hc@px}b 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 Date: Sun, 24 Mar 2024 21:06:34 -0400 Subject: [PATCH 06/46] Load more utf16 characters --- .../renderer/gui/font/glyph/FontGlyphs.kt | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 201671853..b91f120e0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -27,7 +27,10 @@ class FontGlyphs(font: Font) { var y = SPACE var rowHeight = 0 - charList.forEach { char -> + // Because UTF16 takes 2 bytes per character, we can't use the full range of characters + // As the texture size is limited to 2^15, we can only use the first 2^14 characters + // But then we don't want to fill the whole heap, so we limit ourselves to the first 2^13 characters + (Char.MIN_VALUE..'\u1FFF').forEach { char -> val charImage = getCharImage(font, char) val fullWidth = charImage.width + SPACE @@ -41,7 +44,7 @@ class FontGlyphs(font: Font) { rowHeight = 0 } - check(y + fullHeight <= TEXTURE_SIZE) { "Can't load font glyphs. Too small texture size" } + check(y + fullHeight <= TEXTURE_SIZE) { "Can't load font glyphs. Texture size is too small" } graphics.drawImage(charImage, x, y, null) @@ -70,21 +73,15 @@ class FontGlyphs(font: Font) { charMap[char.code] companion object { - private const val TEXTURE_SIZE = 2048 + // The size cannot be bigger than 2^15 because the rasterizer needs to be fed with dimensions that when multiplied together are less than 2^31 + // This can be bypassed by using a custom rasterizer, but it's not worth the effort + // The size is also limited by the java heap size, as the image is stored in memory + // and then uploaded to the GPU + // Since most Lambda users probably have bad pc we will limit it to 512mb of ram when loading + // and in the future we could grow the textures when needed + private const val TEXTURE_SIZE = 8192 private const val SPACE = 2 private const val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE private const val LOD_LEVELS = 4 - - private val latin = ('a'..'z').toList() - private val cyrillic = ('а'..'я').toList() - - private val german = "üÜöÖäÄß".toCharArray().toList() - private val ukrainian = "ґҐїЇєЄ".toCharArray().toList() - - private val special = ((32..47) + (58..64) + (91..96) + (123..126)).map(::Char) - - private val charList = special + - latin + cyrillic + - german + ukrainian } -} \ No newline at end of file +} From 0d5f46b3e5c6d5d7ca857fc6bd105f1c20617e0b Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:19:30 -0400 Subject: [PATCH 07/46] Useless null check --- .../kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt index 6da4e54e2..bdc760446 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -15,7 +15,7 @@ class FontEntry( override var text by owner.field("") override var position by owner.field(Vec2d.ZERO) - override var color by owner.field(Color.WHITE!!) + override var color by owner.field(Color.WHITE) override var scale by owner.field(1.0) override var shadow by owner.field(true) @@ -112,4 +112,4 @@ interface IFontEntry : IRenderEntry { val widthVec get() = Vec2d(width, 0.0) val heightVec get() = Vec2d(0.0, height) -} \ No newline at end of file +} From e19065a1672ae6a1abf9d2a85d3758636f693c1c Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 25 Mar 2024 19:10:29 +0100 Subject: [PATCH 08/46] Resolve conflicts --- common/src/main/kotlin/com/lambda/graphics/RenderMain.kt | 5 +++-- .../kotlin/com/lambda/module/modules/client/Rubberband.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 8ceb9b4a8..edf7ab8f2 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -2,6 +2,7 @@ package com.lambda.graphics import com.lambda.Lambda.mc import com.lambda.event.EventFlow +import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices.translate @@ -22,10 +23,10 @@ object RenderMain { setupGL { rescale(HUD.scale) - EventFlow.post(RenderEvent.GUI.Scaled(HUD.scale)) + RenderEvent.GUI.Scaled(HUD.scale).post() rescale(1.0) - EventFlow.post(RenderEvent.GUI.Fixed()) + RenderEvent.GUI.Fixed().post() } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Rubberband.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Rubberband.kt index fbd2eab2f..7480980b6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/Rubberband.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/Rubberband.kt @@ -8,12 +8,12 @@ import com.lambda.module.tag.ModuleTag import com.lambda.util.Communication.warn import com.lambda.util.math.VecUtils.dist import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.text.Color import com.lambda.util.text.buildText import com.lambda.util.text.color import com.lambda.util.text.literal import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket import net.minecraft.util.math.Vec3d +import java.awt.Color // ToDo: Should also include last packet info as HUD element and connection state. We may find a better name. object Rubberband : Module( From 8e8692ad8153d106cb312ca43cb0eb12697be315 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 25 Mar 2024 19:10:55 +0100 Subject: [PATCH 09/46] Even more conflicts --- .../kotlin/com/lambda/interaction/PlayerPacketManager.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index 8307c0db6..8798b7026 100644 --- a/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -17,13 +17,8 @@ import com.lambda.util.player.MovementUtils.motionZ import com.lambda.util.primitives.extension.component1 import com.lambda.util.primitives.extension.component2 import com.lambda.util.primitives.extension.component3 -import com.lambda.util.text.Color -import com.lambda.util.text.buildText -import com.lambda.util.text.color -import com.lambda.util.text.literal import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.* -import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket import net.minecraft.util.math.MathHelper.square import net.minecraft.util.math.Vec3d From 7b0a31588633fc88d116058e0934298d232e2c4f Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:50:11 -0400 Subject: [PATCH 10/46] Feature: Glyphs setting --- .../renderer/gui/font/FontRenderer.kt | 4 +- .../renderer/gui/font/glyph/FontGlyphs.kt | 75 ++++++++++--------- .../module/modules/client/FontSettings.kt | 3 +- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index ebac6a3f3..ffbfc92a0 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -4,7 +4,7 @@ import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader -class FontRenderer(private val font: LambdaFont = LambdaFont.FiraSansRegular) : AbstractGuiRenderer( +class FontRenderer(private val font: LambdaFont) : AbstractGuiRenderer( VertexAttrib.Group.FONT ) { override fun render() { @@ -19,4 +19,4 @@ class FontRenderer(private val font: LambdaFont = LambdaFont.FiraSansRegular) : companion object { private val shader = Shader("renderer/font") } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index b91f120e0..4cbc14fbe 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -1,7 +1,9 @@ package com.lambda.graphics.renderer.gui.font.glyph +import com.lambda.Lambda import com.lambda.graphics.texture.TextureUtils.getCharImage import com.lambda.graphics.texture.TextureUtils.rescale +import com.lambda.module.modules.client.FontSettings import com.lambda.util.math.Vec2d import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import java.awt.Color @@ -10,6 +12,7 @@ import java.awt.Graphics2D import java.awt.image.BufferedImage import kotlin.math.max import kotlin.math.pow +import kotlin.system.measureTimeMillis class FontGlyphs(font: Font) { private val charMap = Int2ObjectOpenHashMap() @@ -18,53 +21,52 @@ class FontGlyphs(font: Font) { var fontHeight = 0.0; private set init { - val image = BufferedImage(TEXTURE_SIZE, TEXTURE_SIZE, BufferedImage.TYPE_INT_ARGB) + val time = measureTimeMillis { + val image = BufferedImage(TEXTURE_SIZE, TEXTURE_SIZE, BufferedImage.TYPE_INT_ARGB) - val graphics = image.graphics as Graphics2D - graphics.background = Color(0, 0, 0, 0) + val graphics = image.graphics as Graphics2D + graphics.background = Color(0, 0, 0, 0) - var x = SPACE - var y = SPACE - var rowHeight = 0 + var x = 0 + var y = 0 + var rowHeight = 0 - // Because UTF16 takes 2 bytes per character, we can't use the full range of characters - // As the texture size is limited to 2^15, we can only use the first 2^14 characters - // But then we don't want to fill the whole heap, so we limit ourselves to the first 2^13 characters - (Char.MIN_VALUE..'\u1FFF').forEach { char -> - val charImage = getCharImage(font, char) + // Because UTF16 takes 2 bytes per character, we can't use the full range of characters + (Char.MIN_VALUE.. + val charImage = getCharImage(font, char) - val fullWidth = charImage.width + SPACE - val fullHeight = charImage.height + SPACE + rowHeight = max(rowHeight, charImage.height) - rowHeight = max(rowHeight, fullHeight) + if (x + charImage.width >= TEXTURE_SIZE) { + y += rowHeight + x = 0 + rowHeight = 0 + } - if (x + fullWidth >= TEXTURE_SIZE) { - y += rowHeight - x = SPACE - rowHeight = 0 - } + check(y + charImage.height < TEXTURE_SIZE) { "Can't load font glyphs. Texture size is too small" } - check(y + fullHeight <= TEXTURE_SIZE) { "Can't load font glyphs. Texture size is too small" } + graphics.drawImage(charImage, x, y, null) - graphics.drawImage(charImage, x, y, null) + val size = Vec2d(charImage.width, charImage.height) + val uv1 = Vec2d(x, y) * ONE_TEXEL_SIZE + val uv2 = Vec2d(x, y).plus(size) * ONE_TEXEL_SIZE - val size = Vec2d(charImage.width, charImage.height) - val uv1 = Vec2d(x, y) * ONE_TEXEL_SIZE - val uv2 = Vec2d(x, y).plus(size) * ONE_TEXEL_SIZE + charMap[char.code] = CharInfo(size, uv1, uv2) + fontHeight = max(fontHeight, size.y) - charMap[char.code] = CharInfo(size, uv1, uv2) - fontHeight = max(fontHeight, size.y) + x += charImage.width + } - x += charImage.width + SPACE - } + val lodImages = (0..LOD_LEVELS).map { level -> + if (level == 0) return@map image + val size = TEXTURE_SIZE / (2.0.pow(level).toInt()) + rescale(image, size) + } - val lodImages = (0..LOD_LEVELS).map { level -> - if (level == 0) return@map image - val size = TEXTURE_SIZE / (2.0.pow(level).toInt()) - rescale(image, size) + fontTexture = FontTexture(lodImages) } - fontTexture = FontTexture(lodImages) + Lambda.LOG.info("Font ${font.fontName} loaded with ${charMap.size} characters (${time}ms)") } fun bind() = fontTexture.bind() @@ -77,11 +79,10 @@ class FontGlyphs(font: Font) { // This can be bypassed by using a custom rasterizer, but it's not worth the effort // The size is also limited by the java heap size, as the image is stored in memory // and then uploaded to the GPU - // Since most Lambda users probably have bad pc we will limit it to 512mb of ram when loading + // Since most Lambda users probably have bad pc, the default size is 2048, which includes latin, cyrillic, greek and arabic // and in the future we could grow the textures when needed - private const val TEXTURE_SIZE = 8192 - private const val SPACE = 2 - private const val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE + private val TEXTURE_SIZE = FontSettings.amountOfGlyphs * 2 + private val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE private const val LOD_LEVELS = 4 } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt index dd9f35e28..820373dc4 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/FontSettings.kt @@ -13,7 +13,8 @@ object FontSettings : Module( val shadowShift by setting("Shadow Shift", 1.0, 0.0..2.0, 0.05, visibility = { shadow }) val gapSetting by setting("Gap", 1.5, -10.0..10.0, 0.5) val baselineOffset by setting("Vertical Offset", 0.0, -10.0..10.0, 0.5) + val amountOfGlyphs by setting("Glyph Count", 2048, 128..65536, 1) private val lodBiasSetting by setting("Smoothing", -1.0, -10.0..10.0, 0.5) val lodBias get() = lodBiasSetting * 0.25f - 0.5f -} \ No newline at end of file +} From ddfd3f05ce5a98ca554313153fab232c79280765 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 27 Mar 2024 17:58:55 +0300 Subject: [PATCH 11/46] Hot Font Renderer fix --- .../kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt index bdc760446..cad44fba3 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -81,6 +81,8 @@ class FontEntry( val x = position.x val y = position.y + grow(4) + putQuad( vec2(pos1.x + x, pos1.y + y).vec2(ci.uv1.x, ci.uv1.y).color(color).end(), vec2(pos1.x + x, pos2.y + y).vec2(ci.uv1.x, ci.uv2.y).color(color).end(), From 4a45539a2e98ff39839467a4b1df8c03df1484e3 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 30 Mar 2024 21:48:14 +0300 Subject: [PATCH 12/46] TestMe --- .../graphics/renderer/gui/font/FontEntry.kt | 4 +- .../renderer/gui/font/glyph/FontGlyphs.kt | 22 +++---- .../renderer/gui/font/glyph/FontTexture.kt | 31 ---------- .../renderer/gui/font/glyph/MipmapTexture.kt | 35 ++++++++++++ .../lambda/graphics/texture/TextureUtils.kt | 57 ++++++++++--------- .../com/lambda/module/modules/client/HUD.kt | 28 ++++++++- .../shaders/fragment/renderer/font.frag | 6 +- 7 files changed, 106 insertions(+), 77 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/MipmapTexture.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt index cad44fba3..0d3dc4200 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -45,8 +45,8 @@ class FontEntry( val scaledGap = gap * actualScale val shadowColor = getShadowColor(color) - var posX = baselineOffset * actualScale - val posY = height * -0.5 + var posX = 0.0 + val posY = height * -0.5 + baselineOffset * actualScale text.toCharArray().forEach { char -> val charInfo = font[char] ?: return@forEach diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt index 4cbc14fbe..d57b20d8e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontGlyphs.kt @@ -2,7 +2,6 @@ package com.lambda.graphics.renderer.gui.font.glyph import com.lambda.Lambda import com.lambda.graphics.texture.TextureUtils.getCharImage -import com.lambda.graphics.texture.TextureUtils.rescale import com.lambda.module.modules.client.FontSettings import com.lambda.util.math.Vec2d import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap @@ -11,12 +10,11 @@ import java.awt.Font import java.awt.Graphics2D import java.awt.image.BufferedImage import kotlin.math.max -import kotlin.math.pow import kotlin.system.measureTimeMillis class FontGlyphs(font: Font) { private val charMap = Int2ObjectOpenHashMap() - private val fontTexture: FontTexture + private val fontTexture: MipmapTexture var fontHeight = 0.0; private set @@ -33,7 +31,7 @@ class FontGlyphs(font: Font) { // Because UTF16 takes 2 bytes per character, we can't use the full range of characters (Char.MIN_VALUE.. - val charImage = getCharImage(font, char) + val charImage = getCharImage(font, char) ?: return@forEach rowHeight = max(rowHeight, charImage.height) @@ -57,19 +55,18 @@ class FontGlyphs(font: Font) { x += charImage.width } - val lodImages = (0..LOD_LEVELS).map { level -> - if (level == 0) return@map image - val size = TEXTURE_SIZE / (2.0.pow(level).toInt()) - rescale(image, size) - } - - fontTexture = FontTexture(lodImages) + fontTexture = MipmapTexture(image, 4) } Lambda.LOG.info("Font ${font.fontName} loaded with ${charMap.size} characters (${time}ms)") } - fun bind() = fontTexture.bind() + fun bind() { + with(fontTexture) { + bind() + setLOD(FontSettings.lodBias.toFloat()) + } + } fun getChar(char: Char): CharInfo? = charMap[char.code] @@ -83,6 +80,5 @@ class FontGlyphs(font: Font) { // and in the future we could grow the textures when needed private val TEXTURE_SIZE = FontSettings.amountOfGlyphs * 2 private val ONE_TEXEL_SIZE = 1.0 / TEXTURE_SIZE - private const val LOD_LEVELS = 4 } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt deleted file mode 100644 index 987c9fe80..000000000 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/FontTexture.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.lambda.graphics.renderer.gui.font.glyph - -import com.lambda.graphics.texture.Texture -import com.lambda.graphics.texture.TextureUtils.setupLOD -import com.lambda.graphics.texture.TextureUtils.upload -import com.lambda.module.modules.client.FontSettings -import org.lwjgl.opengl.GL14.* -import java.awt.image.BufferedImage - -class FontTexture(private val textures: List) : Texture() { - private var lastLod: Float? = null - - override fun init() { - setupLOD(textures.size) - - textures.forEachIndexed { level: Int, image: BufferedImage -> - upload(image, level) - } - } - - override fun bind() { - super.bind() - - val targetLod = FontSettings.lodBias.toFloat() - - if (lastLod == targetLod) return - lastLod = targetLod - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, targetLod) - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/MipmapTexture.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/MipmapTexture.kt new file mode 100644 index 000000000..3e8b7fc4b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/MipmapTexture.kt @@ -0,0 +1,35 @@ +package com.lambda.graphics.renderer.gui.font.glyph + +import com.lambda.graphics.texture.Texture +import com.lambda.graphics.texture.TextureUtils.rescale +import com.lambda.graphics.texture.TextureUtils.setupLOD +import com.lambda.graphics.texture.TextureUtils.upload +import org.lwjgl.opengl.GL14.* +import java.awt.image.BufferedImage + +class MipmapTexture(private val image: BufferedImage, private val levels: Int = 4) : Texture() { + private var lastLod: Float? = null + + override fun init() { + setupLOD(levels) + + // Upload base image + upload(image, 0) + + // Upload downscaled ones + for (level in 1..levels) { + val newWidth = image.width shr level + val newHeight = image.height shr level + val scaled = image.rescale(newWidth, newHeight) + + upload(scaled, level) + } + } + + fun setLOD(targetLod: Float) { + if (lastLod == targetLod) return + lastLod = targetLod + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, targetLod) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt index eeab5b7dd..5361b4cc3 100644 --- a/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/texture/TextureUtils.kt @@ -1,25 +1,22 @@ package com.lambda.graphics.texture -import com.mojang.blaze3d.platform.GlConst -import com.mojang.blaze3d.platform.GlStateManager import com.mojang.blaze3d.systems.RenderSystem import net.minecraft.client.texture.NativeImage import org.lwjgl.BufferUtils -import org.lwjgl.opengl.GL12C.* -import org.lwjgl.opengl.GL13 +import org.lwjgl.opengl.GL13C.* import java.awt.Color import java.awt.Font import java.awt.RenderingHints +import java.awt.Transparency import java.awt.image.BufferedImage import java.io.ByteArrayOutputStream -import java.nio.IntBuffer import javax.imageio.ImageIO import kotlin.math.roundToInt import kotlin.math.sqrt object TextureUtils { fun bindTexture(id: Int, slot: Int = 0) { - RenderSystem.activeTexture(GL13.GL_TEXTURE0 + slot) + RenderSystem.activeTexture(GL_TEXTURE0 + slot) RenderSystem.bindTexture(id) } @@ -27,12 +24,8 @@ object TextureUtils { val width = bufferedImage.width val height = bufferedImage.height - glTexImage2D(GL_TEXTURE_2D, lod, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as IntBuffer?) - + glTexImage2D(GL_TEXTURE_2D, lod, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, readImage(bufferedImage)) setupTexture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR) - readImage(bufferedImage, lod) - GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_WRAP_S, GlConst.GL_CLAMP_TO_EDGE) - GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GlConst.GL_TEXTURE_WRAP_T, GlConst.GL_CLAMP_TO_EDGE) } fun setupLOD(levels: Int) { @@ -54,7 +47,7 @@ object TextureUtils { glPixelStorei(GL_UNPACK_ALIGNMENT, 4) } - private fun readImage(bufferedImage: BufferedImage, lod: Int) { + private fun readImage(bufferedImage: BufferedImage): Long { val stream = ByteArrayOutputStream() ImageIO.write(bufferedImage, "png", stream) @@ -64,15 +57,12 @@ object TextureUtils { .put(bytes) .flip() - val image = NativeImage.read(buffer) - val width = bufferedImage.width - val height = bufferedImage.height - - glTexSubImage2D(GL_TEXTURE_2D, lod, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image.pointer) - buffer.clear() + return NativeImage.read(buffer).pointer } - fun getCharImage(font: Font, char: Char): BufferedImage { + fun getCharImage(font: Font, char: Char): BufferedImage? { + if (!font.canDisplay(char)) return null + val tempGraphics2D = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).createGraphics() tempGraphics2D.font = font val fontMetrics = tempGraphics2D.fontMetrics @@ -93,26 +83,37 @@ object TextureUtils { return charImage } - fun rescale(imageIn: BufferedImage, targetSize: Int): BufferedImage { - var image = imageIn + fun BufferedImage.rescale(targetWidth: Int, targetHeight: Int): BufferedImage { + val type = if (this.transparency == Transparency.OPAQUE) + BufferedImage.TYPE_INT_RGB + else BufferedImage.TYPE_INT_ARGB - var size = image.width - val divisor = sqrt((size / targetSize).toDouble()) + var image = this + + var width = image.width + var height = image.height + + val divisorX = sqrt((width / targetWidth).toDouble()) + val divisorY = sqrt((height / targetHeight).toDouble()) do { - if (size > targetSize) { - size = (size / divisor).roundToInt().coerceAtLeast(targetSize) + if (width > targetWidth) { + width = (width / divisorX).roundToInt().coerceAtLeast(targetWidth) + } + + if (height > targetHeight) { + height = (height / divisorY).roundToInt().coerceAtLeast(targetHeight) } - val tempImage = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB) + val tempImage = BufferedImage(width, height, type) val graphics2D = tempImage.createGraphics() graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR) - graphics2D.drawImage(image, 0, 0, size, size, null) + graphics2D.drawImage(image, 0, 0, width, height, null) graphics2D.dispose() image = tempImage - } while (size != targetSize) + } while (width != targetWidth || height != targetHeight) return image } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt index a4c9f032a..117c2b6e0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt @@ -4,8 +4,12 @@ import com.lambda.event.events.KeyPressEvent import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.buffer.vao.VAO +import com.lambda.graphics.buffer.vao.vertex.VertexAttrib +import com.lambda.graphics.buffer.vao.vertex.VertexMode import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.font.LambdaFont +import com.lambda.graphics.shader.Shader import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode @@ -42,8 +46,30 @@ object HUD : Module( renderer.update() } + val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) + val shader = Shader("renderer/font") + listener(alwaysListen = true) { - renderer.render() + //renderer.render() + LambdaFont.FiraSansRegular.bind() + shader.use() + + val color = Color.WHITE + val pos1 = Vec2d(10.0, 10.0) + val pos2 = Vec2d(400.0, 400.0) + + vao.use { + putQuad( + vec2(pos1.x, pos1.y).vec2(0.0, 0.0).color(color).end(), + vec2(pos1.x, pos2.y).vec2(0.0, 1.0).color(color).end(), + vec2(pos2.x, pos2.y).vec2(1.0, 1.0).color(color).end(), + vec2(pos2.x, pos1.y).vec2(1.0, 0.0).color(color).end() + ) + + upload() + render() + clear() + } } } } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag index 3522a98c5..15b127cd5 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag @@ -8,6 +8,8 @@ in vec4 v_Color; out vec4 color; void main() { - float alpha = texture(u_Texture, v_TexCoord).a; - color = vec4(v_Color.rgb, v_Color.a * alpha); + /*float alpha = texture(u_Texture, v_TexCoord).a; + color = vec4(v_Color.rgb, v_Color.a * alpha);*/ + + color = texture(u_Texture, v_TexCoord); } \ No newline at end of file From 3777597564d86a1262213a19eb21c1783de5f3b0 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 31 Mar 2024 01:23:09 +0300 Subject: [PATCH 13/46] Cleanup --- .../kotlin/com/lambda/graphics/RenderMain.kt | 8 +-- .../com/lambda/graphics/buffer/vao/VAO.kt | 40 +++++------- .../com/lambda/graphics/gl/GlStateUtils.kt | 6 ++ .../kotlin/com/lambda/graphics/gl/Matrices.kt | 10 ++- .../com/lambda/graphics/renderer/Renderer.kt | 2 +- .../renderer/gui/font/FontRenderer.kt | 2 +- .../graphics/renderer/gui/font/LambdaFont.kt | 1 - .../renderer/gui/font/glyph/FontGlyphs.kt | 3 +- .../graphics/renderer/gui/rect/RectEntry.kt | 3 +- .../font/glyph => texture}/MipmapTexture.kt | 3 +- .../module/modules/client/FontSettings.kt | 6 +- .../com/lambda/module/modules/client/HUD.kt | 63 ------------------- .../lambda/shaders/fragment/pos_color.frag | 8 --- .../shaders/fragment/renderer/font.frag | 6 +- .../lambda/shaders/vertex/pos_color.vert | 14 ----- 15 files changed, 43 insertions(+), 132 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/{renderer/gui/font/glyph => texture}/MipmapTexture.kt (90%) delete mode 100644 common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag delete mode 100644 common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index edf7ab8f2..d74e18936 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -5,20 +5,20 @@ import com.lambda.event.EventFlow import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.graphics.gl.GlStateUtils.setupGL +import com.lambda.graphics.gl.Matrices +import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate import com.lambda.module.modules.client.HUD import net.minecraft.client.util.math.MatrixStack import org.joml.Matrix4f object RenderMain { - var stack = MatrixStack() - val projectionMatrix = Matrix4f() - val modelViewMatrix: Matrix4f get() = stack.peek().positionMatrix + val modelViewMatrix: Matrix4f get() = Matrices.stack.peek().positionMatrix @JvmStatic fun render2D() { - stack = MatrixStack() + resetMatrix() translate(0.0, 0.0, -3000.0) setupGL { diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 087bbf814..471227c0f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -33,26 +33,25 @@ class VAO( private var ibo = 0 private val objectSize: Int - private var verticesPointerStart = 0L private lateinit var vertices: ByteBuffer private var verticesPointer = 0L + private var verticesPosition = 0L private lateinit var indices: ByteBuffer private var indicesPointer = 0L - - private var vertexI = 0 private var indicesCount = 0 - // region Initializing + private var vertexIndex = 0 + init { val stride = attribGroup.stride objectSize = stride * drawMode.indicesCount runOnGameThread { vertices = byteBuffer(objectSize * 256 * 4) - verticesPointerStart = address(vertices) - verticesPointer = verticesPointerStart + verticesPosition = address(vertices) + verticesPointer = verticesPosition indices = byteBuffer(drawMode.indicesCount * 512 * 4) indicesPointer = address(indices) @@ -78,9 +77,7 @@ class VAO( unbindIndexBuffer() } } - // endregion - // region Vertex Attributes override fun vec3(x: Double, y: Double, z: Double): VAO { verticesPointer += vec3(verticesPointer, x, y, z) return this @@ -96,12 +93,8 @@ class VAO( return this } - override fun end(): Int { - return vertexI++ - } - // endregion + override fun end() = vertexIndex++ - // region Vertex Objects override fun putLine(vertex1: Int, vertex2: Int) { growIndices(2) val p = indicesPointer + indicesCount * 4L @@ -133,14 +126,12 @@ class VAO( int(p + 20, vertex1) indicesCount += 6 } - // endregion - // region Memory override fun grow(amount: Int) { val cap = vertices.capacity - if ((vertexI + amount + 1) * objectSize < cap) return + if ((vertexIndex + amount + 1) * objectSize < cap) return - val offset = verticesPointer - verticesPointerStart + val offset = verticesPointer - verticesPosition var newSize = cap * 2 if (newSize % objectSize != 0) newSize += newSize % objectSize val newVertices = byteBuffer(newSize) @@ -150,8 +141,8 @@ class VAO( copy(from, to, offset) vertices = newVertices - verticesPointerStart = address(vertices) - verticesPointer = verticesPointerStart + offset + verticesPosition = address(vertices) + verticesPointer = verticesPosition + offset } private fun growIndices(amount: Int) { @@ -169,20 +160,19 @@ class VAO( indices = newIndices indicesPointer = address(indices) } - // endregion + override fun render() { if (indicesCount <= 0) return + bindVertexArray(vao) drawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT) unbindVertexArray() } override fun upload() { - // Buffer is empty if (indicesCount <= 0) return - // Uploading - val vboData = vertices.limit((verticesPointer - verticesPointerStart).toInt()) + val vboData = vertices.limit((verticesPointer - verticesPosition).toInt()) val iboData = indices.limit(indicesCount * 4) bindVertexBuffer(vbo) @@ -195,8 +185,8 @@ class VAO( } override fun clear() { - verticesPointer = verticesPointerStart - vertexI = 0 + verticesPointer = verticesPosition + vertexIndex = 0 indicesCount = 0 } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt index fc47bd82b..b0e474eed 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/GlStateUtils.kt @@ -32,6 +32,12 @@ object GlStateUtils { lineSmooth(false) } + fun withDepth(block: () -> Unit) { + depthTest(true) + block() + depthTest(false) + } + @JvmStatic fun capSet(id: Int, flag: Boolean) { val field = when (id) { diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt index 7b548b3d8..1932ec531 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Matrices.kt @@ -1,10 +1,10 @@ package com.lambda.graphics.gl -import com.lambda.graphics.RenderMain +import net.minecraft.client.util.math.MatrixStack object Matrices { - private val stack get() = RenderMain.stack - private val matrix get() = RenderMain.modelViewMatrix + var stack = MatrixStack() + private val matrix get() = stack.peek().positionMatrix fun pushMatrix() { stack.push() @@ -14,6 +14,10 @@ object Matrices { stack.push() } + fun resetMatrix() { + stack = MatrixStack() + } + fun translate(x: Double, y: Double, z: Double = 0.0) { matrix.translate(x.toFloat(), y.toFloat(), z.toFloat()) } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt index e82350c64..70df0c0b7 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -6,7 +6,7 @@ import kotlin.properties.Delegates abstract class Renderer > : IRenderer { private val entrySet = mutableSetOf() - private var rebuild = true + private var rebuild = false abstract val vao: VAO protected abstract fun newEntry(block: T.() -> Unit): T diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index ffbfc92a0..a3860eda9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -9,7 +9,7 @@ class FontRenderer(private val font: LambdaFont) : AbstractGuiRenderer(alwaysListen = true) { - if (it.key != KeyCode.K.key) return@listener - - val yLevel = y - - renderer.build { - position = Vec2d(30.0, yLevel) - text = "I Love Lambda <3" - color = Color(130, 130, 250) - } - - y += 30 - } - - listener(alwaysListen = true) { - renderer.update() - } - - val vao = VAO(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) - val shader = Shader("renderer/font") - - listener(alwaysListen = true) { - //renderer.render() - LambdaFont.FiraSansRegular.bind() - shader.use() - - val color = Color.WHITE - val pos1 = Vec2d(10.0, 10.0) - val pos2 = Vec2d(400.0, 400.0) - - vao.use { - putQuad( - vec2(pos1.x, pos1.y).vec2(0.0, 0.0).color(color).end(), - vec2(pos1.x, pos2.y).vec2(0.0, 1.0).color(color).end(), - vec2(pos2.x, pos2.y).vec2(1.0, 1.0).color(color).end(), - vec2(pos2.x, pos1.y).vec2(1.0, 0.0).color(color).end() - ) - - upload() - render() - clear() - } - } - } } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag b/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag deleted file mode 100644 index d4c6f6047..000000000 --- a/common/src/main/resources/assets/lambda/shaders/fragment/pos_color.frag +++ /dev/null @@ -1,8 +0,0 @@ -#version 330 core - -in vec4 v_Color; -out vec4 color; - -void main() { - color = v_Color; -} diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag index 15b127cd5..3522a98c5 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/font.frag @@ -8,8 +8,6 @@ in vec4 v_Color; out vec4 color; void main() { - /*float alpha = texture(u_Texture, v_TexCoord).a; - color = vec4(v_Color.rgb, v_Color.a * alpha);*/ - - color = texture(u_Texture, v_TexCoord); + float alpha = texture(u_Texture, v_TexCoord).a; + color = vec4(v_Color.rgb, v_Color.a * alpha); } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert b/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert deleted file mode 100644 index f71f1481b..000000000 --- a/common/src/main/resources/assets/lambda/shaders/vertex/pos_color.vert +++ /dev/null @@ -1,14 +0,0 @@ -#version 330 core - -layout (location = 0) in vec4 pos; -layout (location = 1) in vec4 color; - -uniform mat4 u_Projection; -uniform mat4 u_ModelView; - -out vec4 v_Color; - -void main() { - gl_Position = u_Projection * u_ModelView * pos; - v_Color = color; -} From 766054fa810a833d41373873860a13e699b3e74d Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 31 Mar 2024 22:27:17 +0300 Subject: [PATCH 14/46] Gui Basics --- .../kotlin/com/lambda/graphics/RenderMain.kt | 5 ++ .../lambda/graphics/animation/Animation.kt | 39 ++++++++++++++ .../graphics/animation/AnimationTicker.kt | 11 ++++ .../com/lambda/graphics/renderer/IRenderer.kt | 10 +--- .../com/lambda/graphics/renderer/Renderer.kt | 1 + .../renderer/gui/font/FontRenderer.kt | 2 +- .../main/kotlin/com/lambda/gui/LambdaGui.kt | 51 +++++++++++++++++++ .../com/lambda/gui/component/Component.kt | 29 +++++++++++ .../lambda/gui/component/WindowComponent.kt | 33 ++++++++++++ .../com/lambda/gui/layer/RenderLayer.kt | 21 ++++++++ .../src/main/kotlin/com/lambda/util/Mouse.kt | 19 +++++++ .../kotlin/com/lambda/util/math/MathUtils.kt | 1 - 12 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt create mode 100644 common/src/main/kotlin/com/lambda/graphics/animation/AnimationTicker.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/LambdaGui.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/component/Component.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt create mode 100644 common/src/main/kotlin/com/lambda/util/Mouse.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index d74e18936..5e38d2020 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -9,6 +9,7 @@ import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate import com.lambda.module.modules.client.HUD +import com.lambda.util.math.Vec2d import net.minecraft.client.util.math.MatrixStack import org.joml.Matrix4f @@ -16,6 +17,8 @@ object RenderMain { val projectionMatrix = Matrix4f() val modelViewMatrix: Matrix4f get() = Matrices.stack.peek().positionMatrix + var screenSize = Vec2d.ONE + @JvmStatic fun render2D() { resetMatrix() @@ -33,8 +36,10 @@ object RenderMain { private fun rescale(factor: Double) { val width = mc.window.framebufferWidth.toFloat() val height = mc.window.framebufferHeight.toFloat() + val scaledWidth = width / factor val scaledHeight = height / factor + screenSize = Vec2d(scaledWidth, scaledHeight) projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, 1000f, 21000f) } diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt new file mode 100644 index 000000000..4aa3fa96d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt @@ -0,0 +1,39 @@ +package com.lambda.graphics.animation + +import com.lambda.Lambda.mc +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.primitives.extension.partialTicks +import kotlin.reflect.KProperty + +class Animation(initialValue: Double, val update: (Double) -> Double) { + private var prevValue = initialValue + private var currValue = initialValue + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = + lerp(prevValue, currValue, mc.partialTicks) + + operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Double) { + prevValue = valueIn + currValue = valueIn + } + + fun tick() { + prevValue = currValue + currValue = update(currValue) + } + + companion object { + fun AnimationTicker.exp(min: Double, max: Double, speed: Double, flag: () -> Boolean) = + Animation(min) { + val target = if (flag()) max else min + lerp(it, target, speed) + }.apply(::register) + + fun AnimationTicker.linear(min: Double, max: Double, step: Double, flag: () -> Boolean) = + Animation(min) { + val target = if (flag()) max else min + target.coerceIn(it - step, it + step) + }.apply(::register) + } +} + diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/AnimationTicker.kt b/common/src/main/kotlin/com/lambda/graphics/animation/AnimationTicker.kt new file mode 100644 index 000000000..1d11d281a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/animation/AnimationTicker.kt @@ -0,0 +1,11 @@ +package com.lambda.graphics.animation + +class AnimationTicker { + private val animations = mutableListOf() + + fun register(animation: Animation) = + animations.add(animation) + + fun tick() = + animations.forEach(Animation::tick) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt index 9b8ccf05d..18be0ab01 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt @@ -1,29 +1,23 @@ package com.lambda.graphics.renderer interface IRenderer > { - val asRenderer get() = this // Downcast - /** * Registers new render entry - * - * Forces the renderer to rebuild itself on the next update tick */ fun build(block: T.() -> Unit): T /** * Removes given entry from the render set - * - * Forces the renderer to rebuild itself on the next update tick */ fun remove(entry: T): T /** - * Performs a draw call + * Performs a draw call and rebuilds VAO before (if needed) */ fun render() /** - * Ticks all render entries and rebuilds VAO if needed + * Updates all render entries */ fun update() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt index 70df0c0b7..90447cdf9 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -7,6 +7,7 @@ import kotlin.properties.Delegates abstract class Renderer > : IRenderer { private val entrySet = mutableSetOf() private var rebuild = false + val asRenderer get() = this as IRenderer abstract val vao: VAO protected abstract fun newEntry(block: T.() -> Unit): T diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt index a3860eda9..a75513daf 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontRenderer.kt @@ -4,7 +4,7 @@ import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader -class FontRenderer(private val font: LambdaFont) : AbstractGuiRenderer( +class FontRenderer(private val font: LambdaFont = LambdaFont.FiraSansRegular) : AbstractGuiRenderer( VertexAttrib.Group.FONT ) { override fun render() { diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt new file mode 100644 index 000000000..17e25ee5f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt @@ -0,0 +1,51 @@ +package com.lambda.gui + +import com.lambda.gui.component.IComponent +import com.lambda.threading.runSafe +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.Screen +import net.minecraft.text.Text + +interface LambdaGui : IComponent { + fun onCloseRequest(): Boolean = true + + fun show() = runSafe { + mc.setScreen(object : Screen(Text.of("Lambda Screen")) { + override fun onDisplayed() { + onShow() + } + + override fun removed() { + onHide() + } + + override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { + onRender() + } + + override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { + onKey(KeyCode(keyCode)) + + if (keyCode == KeyCode.Escape.key && onCloseRequest()) { + this.close() + } + + return true + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouse(Mouse.Button(button), Mouse.Action.Click) + return true + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouse(Mouse.Button(button), Mouse.Action.Release) + return true + } + + override fun shouldPause() = false + }) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/component/Component.kt b/common/src/main/kotlin/com/lambda/gui/component/Component.kt new file mode 100644 index 000000000..28f5f7d06 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/component/Component.kt @@ -0,0 +1,29 @@ +package com.lambda.gui.component + +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +interface IChildComponent : IComponent { + val parent: T +} + +interface IListComponent > : IComponent { + val children: List +} + +interface IRectComponent : IComponent { + val position: Pair +} + +interface IComponent { + fun onShow() + + fun onHide() + + fun onRender() + + fun onKey(key: KeyCode) + + fun onMouse(button: Mouse.Button, action: Mouse.Action) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt new file mode 100644 index 000000000..79ee29f71 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt @@ -0,0 +1,33 @@ +package com.lambda.gui.component + +import com.lambda.gui.layer.RenderLayer +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +abstract class WindowComponent : IRectComponent { + override var position = Vec2d.ZERO to Vec2d.ZERO + + abstract val title: String + val render = RenderLayer() + + override fun onShow() { + + } + + override fun onHide() { + + } + + override fun onRender() { + + } + + override fun onKey(key: KeyCode) { + + } + + override fun onMouse(button: Mouse.Button, action: Mouse.Action) { + + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt new file mode 100644 index 000000000..3db7221a8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt @@ -0,0 +1,21 @@ +package com.lambda.gui.layer + +import com.lambda.graphics.renderer.IRenderer +import com.lambda.graphics.renderer.gui.font.FontRenderer +import com.lambda.graphics.renderer.gui.rect.RectRenderer + +class RenderLayer { + private val renderers = mutableListOf>() + + val rect = RectRenderer().apply(::register) + val font = FontRenderer().apply(::register) + + fun register(renderer: IRenderer<*>) { + renderers.add(renderer) + } + + fun render() { + renderers.forEach(IRenderer<*>::update) + renderers.forEach(IRenderer<*>::render) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/Mouse.kt b/common/src/main/kotlin/com/lambda/util/Mouse.kt new file mode 100644 index 000000000..b7d9af4d2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/Mouse.kt @@ -0,0 +1,19 @@ +package com.lambda.util + +import org.lwjgl.glfw.GLFW + +class Mouse { + @JvmInline + value class Button(val key: Int) { + companion object { + val Left = Button(GLFW.GLFW_MOUSE_BUTTON_LEFT) + val Right = Button(GLFW.GLFW_MOUSE_BUTTON_RIGHT) + val Middle = Button(GLFW.GLFW_MOUSE_BUTTON_MIDDLE) + } + } + + enum class Action { + Click, + Release + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index 3927758d2..08c2f32d6 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -125,7 +125,6 @@ object MathUtils { lerp(start.y, end.y, factor) ) - fun lerp(start: Rotation, end: Rotation, factor: Double) = Rotation( lerp(start.yaw, end.yaw, factor), From 6116e2cec500dc7cd10f94170adf5b0b1fa58acd Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:42:02 -0400 Subject: [PATCH 15/46] Use safe context --- common/src/main/kotlin/com/lambda/gui/LambdaGui.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt index 17e25ee5f..3745f16d5 100644 --- a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt @@ -1,5 +1,6 @@ package com.lambda.gui +import com.lambda.context.SafeContext import com.lambda.gui.component.IComponent import com.lambda.threading.runSafe import com.lambda.util.KeyCode @@ -11,7 +12,7 @@ import net.minecraft.text.Text interface LambdaGui : IComponent { fun onCloseRequest(): Boolean = true - fun show() = runSafe { + fun SafeContext.show() { mc.setScreen(object : Screen(Text.of("Lambda Screen")) { override fun onDisplayed() { onShow() @@ -48,4 +49,4 @@ interface LambdaGui : IComponent { override fun shouldPause() = false }) } -} \ No newline at end of file +} From f383f9584e1b5fb4e445c4f1073ac43d3a6af65b Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:54:59 -0400 Subject: [PATCH 16/46] one liner --- common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt index 3db7221a8..82faa3ade 100644 --- a/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt @@ -10,12 +10,10 @@ class RenderLayer { val rect = RectRenderer().apply(::register) val font = FontRenderer().apply(::register) - fun register(renderer: IRenderer<*>) { - renderers.add(renderer) - } + fun register(renderer: IRenderer<*>) = renderers.add(renderer) fun render() { renderers.forEach(IRenderer<*>::update) renderers.forEach(IRenderer<*>::render) } -} \ No newline at end of file +} From 728676f8455f916ec4abd1f1eaa5c2f672044fe7 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:16:07 -0400 Subject: [PATCH 17/46] Reverted safe context --- common/src/main/kotlin/com/lambda/gui/LambdaGui.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt index 3745f16d5..5f8f8cc14 100644 --- a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt @@ -1,6 +1,5 @@ package com.lambda.gui -import com.lambda.context.SafeContext import com.lambda.gui.component.IComponent import com.lambda.threading.runSafe import com.lambda.util.KeyCode @@ -12,7 +11,8 @@ import net.minecraft.text.Text interface LambdaGui : IComponent { fun onCloseRequest(): Boolean = true - fun SafeContext.show() { + // We need to find an alternative to this + fun show() = runSafe { mc.setScreen(object : Screen(Text.of("Lambda Screen")) { override fun onDisplayed() { onShow() From d209c78a7849eda8d46931ffd0abd9b211c5b1ae Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Mon, 1 Apr 2024 20:36:40 +0300 Subject: [PATCH 18/46] Adapted baseline offset --- .../kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt | 2 +- .../kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt index 0d3dc4200..ade50fe8e 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/FontEntry.kt @@ -71,7 +71,7 @@ class FontEntry( shadowSetting = FontSettings.shadow shadowBrightness = FontSettings.shadowBrightness shadowShift = FontSettings.shadowShift * 4.0 - baselineOffset = FontSettings.baselineOffset * 2.0f - 20f + baselineOffset = FontSettings.baselineOffset * 2.0f - 10f gap = FontSettings.gapSetting * 0.5f - 0.8f super.update() diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt index 3fe8d034f..6dd284b59 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt @@ -44,8 +44,8 @@ class RectEntry( val round = min(roundRadius, minSize) - val p1 = pos1 - 0.75 - val p2 = pos2 + 0.75 + val p1 = pos1 - 0.5 + val p2 = pos2 + 0.5 grow(4) From be335d501469787f4daad3e49fb25065a88dc830 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Mon, 1 Apr 2024 20:42:18 +0300 Subject: [PATCH 19/46] Destroyable Renderers --- .../com/lambda/graphics/buffer/vao/VAO.kt | 26 ++++++++++------ .../com/lambda/graphics/renderer/IRenderer.kt | 5 +++ .../com/lambda/graphics/renderer/Renderer.kt | 31 ++++++++++++++++--- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 471227c0f..9a27b6c68 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -50,8 +50,8 @@ class VAO( runOnGameThread { vertices = byteBuffer(objectSize * 256 * 4) - verticesPosition = address(vertices) - verticesPointer = verticesPosition + verticesPointer = address(vertices) + verticesPosition = verticesPointer indices = byteBuffer(drawMode.indicesCount * 512 * 4) indicesPointer = address(indices) @@ -79,17 +79,17 @@ class VAO( } override fun vec3(x: Double, y: Double, z: Double): VAO { - verticesPointer += vec3(verticesPointer, x, y, z) + verticesPosition += vec3(verticesPosition, x, y, z) return this } override fun vec2(x: Double, y: Double): VAO { - verticesPointer += vec2(verticesPointer, x, y) + verticesPosition += vec2(verticesPosition, x, y) return this } override fun color(color: Color): VAO { - verticesPointer += color(verticesPointer, color) + verticesPosition += color(verticesPosition, color) return this } @@ -131,7 +131,7 @@ class VAO( val cap = vertices.capacity if ((vertexIndex + amount + 1) * objectSize < cap) return - val offset = verticesPointer - verticesPosition + val offset = verticesPosition - verticesPointer var newSize = cap * 2 if (newSize % objectSize != 0) newSize += newSize % objectSize val newVertices = byteBuffer(newSize) @@ -141,8 +141,8 @@ class VAO( copy(from, to, offset) vertices = newVertices - verticesPosition = address(vertices) - verticesPointer = verticesPosition + offset + verticesPointer = address(vertices) + verticesPosition = verticesPointer + offset } private fun growIndices(amount: Int) { @@ -172,7 +172,7 @@ class VAO( override fun upload() { if (indicesCount <= 0) return - val vboData = vertices.limit((verticesPointer - verticesPosition).toInt()) + val vboData = vertices.limit((verticesPosition - verticesPointer).toInt()) val iboData = indices.limit(indicesCount * 4) bindVertexBuffer(vbo) @@ -185,8 +185,14 @@ class VAO( } override fun clear() { - verticesPointer = verticesPosition + verticesPosition = verticesPointer vertexIndex = 0 indicesCount = 0 } + + fun destroy() { + glDeleteBuffers(ibo) + glDeleteBuffers(vbo) + glDeleteVertexArrays(vao) + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt index 18be0ab01..31cb9f6f1 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/IRenderer.kt @@ -25,4 +25,9 @@ interface IRenderer > { * Clears the render set */ fun clear() + + /** + * Destroys this renderer and frees v-ram used by VAO + */ + fun destroy() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt index 90447cdf9..a806576ae 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/Renderer.kt @@ -7,18 +7,26 @@ import kotlin.properties.Delegates abstract class Renderer > : IRenderer { private val entrySet = mutableSetOf() private var rebuild = false + private var destroyed = false + val asRenderer get() = this as IRenderer abstract val vao: VAO protected abstract fun newEntry(block: T.() -> Unit): T - override fun build(block: T.() -> Unit) = - newEntry(block).process(entrySet::add) + override fun build(block: T.() -> Unit): T { + checkDestroyed() + return newEntry(block).process(entrySet::add) + } - override fun remove(entry: T) = - entry.process(entrySet::remove) + override fun remove(entry: T): T { + checkDestroyed() + return entry.process(entrySet::remove) + } override fun render() { + checkDestroyed() + if (rebuild) { rebuild = false @@ -31,14 +39,25 @@ abstract class Renderer > : IRenderer { } override fun update() { + checkDestroyed() entrySet.forEach(IRenderEntry::update) } override fun clear() { + checkDestroyed() + entrySet.clear() vao.clear() } + override fun destroy() { + checkDestroyed() + + entrySet.clear() + vao.destroy() + destroyed = true + } + private fun T.process(action: T.() -> Unit): T { this.apply(action) rebuild = true @@ -51,4 +70,8 @@ abstract class Renderer > : IRenderer { if (prev == curr) return@observable rebuild = true } + + private fun checkDestroyed() { + check(!destroyed) { "Using the renderer after it is destroyed" } + } } \ No newline at end of file From 5ca76a796f42ba17a0ad9dd700a3c93a068f5867 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Mon, 1 Apr 2024 20:43:20 +0300 Subject: [PATCH 20/46] Load glyphs on startup --- .../src/main/kotlin/com/lambda/core/Loader.kt | 4 +++- .../graphics/renderer/gui/font/LambdaFont.kt | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index db18ddea3..dd9bfa7b6 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -3,6 +3,7 @@ package com.lambda.core import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.command.CommandManager +import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry @@ -13,7 +14,8 @@ object Loader { ModuleRegistry, CommandManager, RotationManager, - PlayerPacketManager + PlayerPacketManager, + LambdaFont.Loader ) fun initialize() { diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt index b4710dd9b..7651d7704 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaFont.kt @@ -1,20 +1,29 @@ package com.lambda.graphics.renderer.gui.font +import com.lambda.core.Loadable import com.lambda.graphics.renderer.gui.font.glyph.FontGlyphs import com.lambda.util.LambdaResource import java.awt.Font -enum class LambdaFont(fontName: String) { +enum class LambdaFont(private val fontName: String) { FiraSansRegular("FiraSans-Regular"), FiraSansBold("FiraSans-Bold"); - val glyphs = FontGlyphs(getFont(fontName)) + lateinit var glyphs: FontGlyphs operator fun get(char: Char) = glyphs.getChar(char) - private fun getFont(name: String): Font { - val resource = LambdaResource("fonts/$name.ttf") - val stream = resource.stream ?: throw IllegalStateException("Failed to locate font $name") - return Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(64.0f) + fun loadGlyphs() { + val resource = LambdaResource("fonts/$fontName.ttf") + val stream = resource.stream ?: throw IllegalStateException("Failed to locate font $fontName") + val font = Font.createFont(Font.TRUETYPE_FONT, stream).deriveFont(64.0f) + glyphs = FontGlyphs(font) + } + + object Loader : Loadable { + override fun load(): String { + entries.forEach(LambdaFont::loadGlyphs) + return "Loaded ${entries.size} fonts" + } } } \ No newline at end of file From a574797866cdbdce3a38564356ba1b316a3a9a9e Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sat, 6 Apr 2024 21:17:47 +0300 Subject: [PATCH 21/46] Shader transparency fix --- .../resources/assets/lambda/shaders/fragment/renderer/rect.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag index d0bdf3952..c01cac634 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag @@ -12,7 +12,7 @@ out vec4 color; void main() { vec2 halfSize = v_Size * 0.5; - float radius = v_RoundRadius; + float radius = max(v_RoundRadius, SMOOTHING); vec2 smoothVec = vec2(SMOOTHING); vec2 coord = mix(-smoothVec, v_Size + smoothVec, v_TexCoord); From b268015810d0d66308a19b5e15392bc5625f8433 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 11:53:29 +0300 Subject: [PATCH 22/46] Exp animation clamp --- .../lambda/graphics/animation/Animation.kt | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt index 4aa3fa96d..fe22a2895 100644 --- a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt +++ b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt @@ -3,6 +3,7 @@ package com.lambda.graphics.animation import com.lambda.Lambda.mc import com.lambda.util.math.MathUtils.lerp import com.lambda.util.primitives.extension.partialTicks +import kotlin.math.abs import kotlin.reflect.KProperty class Animation(initialValue: Double, val update: (Double) -> Double) { @@ -24,9 +25,13 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { companion object { fun AnimationTicker.exp(min: Double, max: Double, speed: Double, flag: () -> Boolean) = - Animation(min) { - val target = if (flag()) max else min - lerp(it, target, speed) + exp({ min }, { max }, speed, flag) + + fun AnimationTicker.exp(min: () -> Double, max: () -> Double, speed: Double, flag: () -> Boolean) = + Animation(min()) { + val target = if (flag()) max() else min() + if (abs(target - it) < CLAMP) target + else lerp(it, target, speed) }.apply(::register) fun AnimationTicker.linear(min: Double, max: Double, step: Double, flag: () -> Boolean) = @@ -34,6 +39,15 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { val target = if (flag()) max else min target.coerceIn(it - step, it + step) }.apply(::register) + + fun AnimationTicker.linear(min: () -> Double, max: () -> Double, step: Double, flag: () -> Boolean) = + Animation(min()) { + val target = if (flag()) max() else min() + target.coerceIn(it - step, it + step) + }.apply(::register) + + // Exponent animation will never reach target value + private const val CLAMP = 0.01 } } From e768dce729f33453b7248895cefc848dbed28863 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 11:58:42 +0300 Subject: [PATCH 23/46] Rect impl, Scissor utils, refactor --- .../com/lambda/event/events/RenderEvent.kt | 4 ++ .../kotlin/com/lambda/graphics/RenderMain.kt | 6 -- .../com/lambda/graphics/buffer/vao/VAO.kt | 16 ++--- .../graphics/gl/{MemoryUtils.kt => Memory.kt} | 2 +- .../kotlin/com/lambda/graphics/gl/Scissor.kt | 64 +++++++++++++++++++ .../graphics/renderer/gui/rect/RectEntry.kt | 17 ++--- .../src/main/kotlin/com/lambda/util/Mouse.kt | 2 + .../main/kotlin/com/lambda/util/math/Rect.kt | 41 ++++++++++++ 8 files changed, 127 insertions(+), 25 deletions(-) rename common/src/main/kotlin/com/lambda/graphics/gl/{MemoryUtils.kt => Memory.kt} (98%) create mode 100644 common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt create mode 100644 common/src/main/kotlin/com/lambda/util/math/Rect.kt diff --git a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt index 00613b20d..178163df5 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RenderEvent.kt @@ -1,8 +1,10 @@ package com.lambda.event.events +import com.lambda.Lambda.mc import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable +import com.lambda.util.math.Vec2d abstract class RenderEvent : Event { class World : RenderEvent() @@ -10,6 +12,8 @@ abstract class RenderEvent : Event { abstract class GUI(val scaleFactor: Double) : RenderEvent() { class Scaled(scaleFactor: Double) : GUI(scaleFactor) class Fixed : GUI(1.0) + + val screenSize = Vec2d(mc.window.framebufferWidth, mc.window.framebufferHeight) / scaleFactor } class UpdateTarget : RenderEvent(), ICancellable by Cancellable() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 5e38d2020..3ec5d7641 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -1,7 +1,6 @@ package com.lambda.graphics import com.lambda.Lambda.mc -import com.lambda.event.EventFlow import com.lambda.event.EventFlow.post import com.lambda.event.events.RenderEvent import com.lambda.graphics.gl.GlStateUtils.setupGL @@ -9,16 +8,12 @@ import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate import com.lambda.module.modules.client.HUD -import com.lambda.util.math.Vec2d -import net.minecraft.client.util.math.MatrixStack import org.joml.Matrix4f object RenderMain { val projectionMatrix = Matrix4f() val modelViewMatrix: Matrix4f get() = Matrices.stack.peek().positionMatrix - var screenSize = Vec2d.ONE - @JvmStatic fun render2D() { resetMatrix() @@ -39,7 +34,6 @@ object RenderMain { val scaledWidth = width / factor val scaledHeight = height / factor - screenSize = Vec2d(scaledWidth, scaledHeight) projectionMatrix.setOrtho(0f, scaledWidth.toFloat(), scaledHeight.toFloat(), 0f, 1000f, 21000f) } diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 9a27b6c68..dfec78dc7 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -2,14 +2,14 @@ package com.lambda.graphics.buffer.vao import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.buffer.vao.vertex.VertexMode -import com.lambda.graphics.gl.MemoryUtils.address -import com.lambda.graphics.gl.MemoryUtils.byteBuffer -import com.lambda.graphics.gl.MemoryUtils.capacity -import com.lambda.graphics.gl.MemoryUtils.color -import com.lambda.graphics.gl.MemoryUtils.copy -import com.lambda.graphics.gl.MemoryUtils.int -import com.lambda.graphics.gl.MemoryUtils.vec2 -import com.lambda.graphics.gl.MemoryUtils.vec3 +import com.lambda.graphics.gl.Memory.address +import com.lambda.graphics.gl.Memory.byteBuffer +import com.lambda.graphics.gl.Memory.capacity +import com.lambda.graphics.gl.Memory.color +import com.lambda.graphics.gl.Memory.copy +import com.lambda.graphics.gl.Memory.int +import com.lambda.graphics.gl.Memory.vec2 +import com.lambda.graphics.gl.Memory.vec3 import com.lambda.graphics.gl.VaoUtils import com.lambda.graphics.gl.VaoUtils.bindIndexBuffer import com.lambda.graphics.gl.VaoUtils.bindVertexArray diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt similarity index 98% rename from common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt rename to common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt index c340b48fc..95374e8b8 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/MemoryUtils.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Memory.kt @@ -7,7 +7,7 @@ import java.awt.Color import java.nio.Buffer import java.nio.ByteBuffer -object MemoryUtils { +object Memory { private val vec2Size = VertexAttrib.Vec2.size private val vec3Size = VertexAttrib.Vec3.size private val colorSize = VertexAttrib.Color.size diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt new file mode 100644 index 000000000..d7afae594 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt @@ -0,0 +1,64 @@ +package com.lambda.graphics.gl + +import com.lambda.Lambda.mc +import com.lambda.module.modules.client.HUD +import com.lambda.util.math.MathUtils.ceilToInt +import com.lambda.util.math.MathUtils.floorToInt +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d +import com.mojang.blaze3d.systems.RenderSystem.disableScissor +import com.mojang.blaze3d.systems.RenderSystem.enableScissor +import kotlin.math.max +import kotlin.math.min + +object Scissor { + private var stack = ArrayDeque() + + fun scissor(rect: Rect, block: () -> Unit) = scissor(rect.leftTop, rect.rightBottom, block) + + fun scissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) { + stack.lastOrNull()?.let { + registerScissor( + // clamp corners so children scissor box can't overlap parent + Vec2d(max(pos1.x, it.pos1.x), max(pos1.y, it.pos1.y)), + Vec2d(min(pos2.x, it.pos2.x), min(pos2.y, it.pos2.y)), + block + ) + } ?: registerScissor(pos1, pos2, block) + } + + private fun registerScissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) { + scissor(Entry(pos1, pos2)) + + block() + + stack.removeLast() + stack.lastOrNull().apply(::scissor) + } + + private fun scissor(entry: Entry?) { + if (entry == null) { + disableScissor() + return + } + + stack.add(entry) + + val pos1 = entry.pos1 * HUD.scale + val pos2 = entry.pos2 * HUD.scale + + val width = max(pos2.x - pos1.x, 0.0) + val height = max(pos2.y - pos1.y, 0.0) + + val y = mc.window.framebufferHeight - pos1.y - height + + enableScissor( + pos1.x.floorToInt(), + y.floorToInt(), + width.ceilToInt(), + height.ceilToInt() + ) + } + + private data class Entry(val pos1: Vec2d, val pos2: Vec2d) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt index 6dd284b59..c498fb12c 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt @@ -2,7 +2,7 @@ package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.vao.IRenderContext import com.lambda.graphics.renderer.IRenderEntry -import com.lambda.util.math.Vec2d +import com.lambda.util.math.Rect import java.awt.Color import kotlin.math.min @@ -10,7 +10,7 @@ class RectEntry( override val owner: RectRenderer, override val updateBlock: IRectEntry.() -> Unit ) : IRectEntry { - override var position by owner.field(Vec2d.ZERO to Vec2d.ZERO) + override var position by owner.field(Rect.ZERO) override var roundRadius by owner.field(0.0) @@ -33,16 +33,16 @@ class RectEntry( leftBottom .alpha < MIN_ALPHA ) return@use - val pos1 = position.first - val pos2 = position.second + val pos1 = position.leftTop + val pos2 = position.rightBottom val size = pos2 - pos1 if (size.x < MIN_SIZE || size.y < MIN_SIZE) return@use val halfSize = size * 0.5 - val minSize = min(halfSize.x, halfSize.y) + val maxRadius = min(halfSize.x, halfSize.y) - val round = min(roundRadius, minSize) + val round = min(roundRadius, maxRadius) val p1 = pos1 - 0.5 val p2 = pos2 + 0.5 @@ -64,10 +64,7 @@ class RectEntry( } interface IRectEntry : IRenderEntry { - var position: Pair - - val size get() = position.second - position.first - val center get() = position.first + size * 0.5 + var position: Rect var roundRadius: Double diff --git a/common/src/main/kotlin/com/lambda/util/Mouse.kt b/common/src/main/kotlin/com/lambda/util/Mouse.kt index b7d9af4d2..01b5c940e 100644 --- a/common/src/main/kotlin/com/lambda/util/Mouse.kt +++ b/common/src/main/kotlin/com/lambda/util/Mouse.kt @@ -10,6 +10,8 @@ class Mouse { val Right = Button(GLFW.GLFW_MOUSE_BUTTON_RIGHT) val Middle = Button(GLFW.GLFW_MOUSE_BUTTON_MIDDLE) } + + val isMainButton get() = key == GLFW.GLFW_MOUSE_BUTTON_LEFT || key == GLFW.GLFW_MOUSE_BUTTON_RIGHT } enum class Action { diff --git a/common/src/main/kotlin/com/lambda/util/math/Rect.kt b/common/src/main/kotlin/com/lambda/util/math/Rect.kt new file mode 100644 index 000000000..bcc14b84d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/math/Rect.kt @@ -0,0 +1,41 @@ +package com.lambda.util.math + +import com.lambda.util.math.MathUtils.lerp + +data class Rect(private val pos1: Vec2d, private val pos2: Vec2d) { + constructor(size: Vec2d) : this(Vec2d.ZERO, size) + + val left = pos1.x + val top = pos1.y + val right = pos2.x + val bottom = pos2.y + + val leftTop get() = Vec2d(left, top) + val rightTop get() = Vec2d(right, top) + val rightBottom get() = Vec2d(right, bottom) + val leftBottom get() = Vec2d(left, bottom) + + val size get() = Vec2d(right - left, bottom - top) + val center get() = lerp(pos1, pos2, 0.5) + + operator fun plus(vec2d: Vec2d) = Rect(pos1 + vec2d, pos2 + vec2d) + operator fun minus(vec2d: Vec2d) = Rect(pos1 - vec2d, pos2 - vec2d) + + fun moveFirst(vec2d: Vec2d) = Rect(pos1 + vec2d, pos2) + fun moveSecond(vec2d: Vec2d) = Rect(pos1, pos2 + vec2d) + + fun extend(amount: Double) = Rect(pos1 - amount, pos2 + amount) + fun shrink(amount: Double) = extend(-amount) + + fun contains(point: Vec2d) = point.x in left..right && point.y in top..bottom + + companion object { + val ZERO = Rect(Vec2d.ZERO, Vec2d.ZERO) + + fun basedOn(base: Vec2d, width: Double, height: Double) = + Rect(base, base + Vec2d(width, height)) + + fun basedOn(base: Vec2d, size: Vec2d) = + Rect(base, base + size) + } +} \ No newline at end of file From 86c938651cf344f227afe483417e3eab26ac1340 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 18:35:03 +0300 Subject: [PATCH 24/46] Windows, Buttons and tests --- .../lambda/graphics/animation/Animation.kt | 29 ++--- .../graphics/renderer/gui/rect/RectEntry.kt | 4 +- .../main/kotlin/com/lambda/gui/LambdaGui.kt | 52 -------- .../kotlin/com/lambda/gui/api/LambdaGui.kt | 96 ++++++++++++++ .../gui/api/component/InteractiveComponent.kt | 35 ++++++ .../gui/api/component/WindowComponent.kt | 118 ++++++++++++++++++ .../gui/api/component/core/IComponent.kt | 23 ++++ .../gui/api/component/core/IListComponent.kt | 54 ++++++++ .../gui/api/component/core/IRectComponent.kt | 7 ++ .../gui/api/component/sub/ButtonComponent.kt | 66 ++++++++++ .../lambda/gui/{ => api}/layer/RenderLayer.kt | 10 +- .../com/lambda/gui/component/Component.kt | 29 ----- .../lambda/gui/component/WindowComponent.kt | 33 ----- .../gui/impl/clickgui/LambdaClickGui.kt | 47 +++++++ .../lambda/module/modules/client/ClickGui.kt | 45 +++++++ .../main/kotlin/com/lambda/util/math/Rect.kt | 16 ++- 16 files changed, 523 insertions(+), 141 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/gui/LambdaGui.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt rename common/src/main/kotlin/com/lambda/gui/{ => api}/layer/RenderLayer.kt (63%) delete mode 100644 common/src/main/kotlin/com/lambda/gui/component/Component.kt delete mode 100644 common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt index fe22a2895..6afd4908f 100644 --- a/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt +++ b/common/src/main/kotlin/com/lambda/graphics/animation/Animation.kt @@ -24,30 +24,27 @@ class Animation(initialValue: Double, val update: (Double) -> Double) { } companion object { - fun AnimationTicker.exp(min: Double, max: Double, speed: Double, flag: () -> Boolean) = + fun AnimationTicker.exp(min: () -> Double, max: () -> Double, speed: Double, flag: () -> Boolean) = + exp(min, max, { speed }, flag) + + fun AnimationTicker.exp(min: Double, max: Double, speed: () -> Double, flag: () -> Boolean) = exp({ min }, { max }, speed, flag) - fun AnimationTicker.exp(min: () -> Double, max: () -> Double, speed: Double, flag: () -> Boolean) = - Animation(min()) { - val target = if (flag()) max() else min() - if (abs(target - it) < CLAMP) target - else lerp(it, target, speed) - }.apply(::register) + fun AnimationTicker.exp(min: Double, max: Double, speed: Double, flag: () -> Boolean) = + exp({ min }, { max }, { speed }, flag) - fun AnimationTicker.linear(min: Double, max: Double, step: Double, flag: () -> Boolean) = - Animation(min) { + @Suppress("NAME_SHADOWING") + fun AnimationTicker.exp(min: () -> Double, max: () -> Double, speed: () -> Double, flag: () -> Boolean) = + Animation(min()) { + val min = min(); val max = max() val target = if (flag()) max else min - target.coerceIn(it - step, it + step) - }.apply(::register) - fun AnimationTicker.linear(min: () -> Double, max: () -> Double, step: Double, flag: () -> Boolean) = - Animation(min()) { - val target = if (flag()) max() else min() - target.coerceIn(it - step, it + step) + if (abs(target - it) < CLAMP * abs(max - min)) target + else lerp(it, target, speed()) }.apply(::register) // Exponent animation will never reach target value - private const val CLAMP = 0.01 + private const val CLAMP = 0.001 } } diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt index c498fb12c..29c184d27 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt @@ -44,8 +44,8 @@ class RectEntry( val round = min(roundRadius, maxRadius) - val p1 = pos1 - 0.5 - val p2 = pos2 + 0.5 + val p1 = pos1 - 0.75 + val p2 = pos2 + 0.75 grow(4) diff --git a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt deleted file mode 100644 index 5f8f8cc14..000000000 --- a/common/src/main/kotlin/com/lambda/gui/LambdaGui.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.lambda.gui - -import com.lambda.gui.component.IComponent -import com.lambda.threading.runSafe -import com.lambda.util.KeyCode -import com.lambda.util.Mouse -import net.minecraft.client.gui.DrawContext -import net.minecraft.client.gui.screen.Screen -import net.minecraft.text.Text - -interface LambdaGui : IComponent { - fun onCloseRequest(): Boolean = true - - // We need to find an alternative to this - fun show() = runSafe { - mc.setScreen(object : Screen(Text.of("Lambda Screen")) { - override fun onDisplayed() { - onShow() - } - - override fun removed() { - onHide() - } - - override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { - onRender() - } - - override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - onKey(KeyCode(keyCode)) - - if (keyCode == KeyCode.Escape.key && onCloseRequest()) { - this.close() - } - - return true - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouse(Mouse.Button(button), Mouse.Action.Click) - return true - } - - override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouse(Mouse.Button(button), Mouse.Action.Release) - return true - } - - override fun shouldPause() = false - }) - } -} diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt new file mode 100644 index 000000000..426c7c6be --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -0,0 +1,96 @@ +package com.lambda.gui.api + +import com.lambda.Lambda.mc +import com.lambda.event.EventFlow +import com.lambda.event.events.RenderEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.gui.api.component.core.IComponent +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.Nameable +import com.lambda.util.math.Vec2d +import com.mojang.blaze3d.systems.RenderSystem.recordRenderCall +import net.minecraft.client.gui.DrawContext +import net.minecraft.client.gui.screen.Screen +import net.minecraft.text.Text + +interface LambdaGui : IComponent, Nameable { + fun onCloseRequest(): Boolean = true + + /** + * Shows this gui screen + * + * No safe context required (TODO: let user open clickgui via main menu) + */ + fun show() = recordRenderCall { + mc.setScreen(object : Screen(Text.of(name)) { + private var screenSize = Vec2d.ZERO + + private val renderListener = listener { event -> + screenSize = event.screenSize + onRender() + } + + private val tickListener = listener { + onTick() + } + + override fun onDisplayed() { + onShow() + } + + override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { + // Let's remove background tint + } + + override fun removed() { + onHide() + + with(EventFlow.syncListeners) { + unsubscribe(renderListener) + unsubscribe(tickListener) + } + } + + override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { + onKey(KeyCode(keyCode)) + + if (keyCode == KeyCode.Escape.key && onCloseRequest()) { + this.close() + } + + return true + } + + override fun charTyped(chr: Char, modifiers: Int): Boolean { + onChar(chr) + return true + } + + override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouseClick(Mouse.Button(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY)) + return true + } + + override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouseClick(Mouse.Button(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY)) + return true + } + + override fun mouseMoved(mouseX: Double, mouseY: Double) { + onMouseMove(rescaleMouse(mouseX, mouseY)) + } + + override fun shouldPause() = false + + private fun rescaleMouse(mouseX: Double, mouseY: Double): Vec2d { + val mcMouse = Vec2d(mouseX, mouseY) + val mcWindow = Vec2d(mc.window.scaledWidth, mc.window.scaledHeight) + + val uv = mcMouse / mcWindow + return uv * screenSize + } + }) + } +} diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt new file mode 100644 index 000000000..9519edbb8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt @@ -0,0 +1,35 @@ +package com.lambda.gui.api.component + +import com.lambda.gui.api.component.core.IRectComponent +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +abstract class InteractiveComponent : IRectComponent { + protected var hovered = false + protected var pressed = false; set(value) { + if (field == value) return + field = value + + if (value) onPress() + else onRelease() + } + + protected var activeMouseButton: Mouse.Button? = null + + protected open fun onPress() {} + protected open fun onRelease() {} + + override fun onShow() { + hovered = false + pressed = false + } + + override fun onMouseMove(mouse: Vec2d) { + hovered = rect.contains(mouse) + } + + override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { + activeMouseButton = button.takeUnless { it.isMainButton && action == Mouse.Action.Click } + pressed = hovered && button.isMainButton && action == Mouse.Action.Click + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt new file mode 100644 index 000000000..341793069 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -0,0 +1,118 @@ +package com.lambda.gui.api.component + +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.graphics.animation.AnimationTicker +import com.lambda.graphics.gl.Scissor.scissor +import com.lambda.graphics.renderer.gui.font.IFontEntry +import com.lambda.gui.api.component.core.IListComponent +import com.lambda.gui.api.component.core.IRectComponent +import com.lambda.gui.api.layer.RenderLayer +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.MathUtils.toInt +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d + +abstract class WindowComponent : InteractiveComponent(), IListComponent { + abstract val title: String + + abstract var width: Double + abstract var height: Double + + var position = Vec2d.ZERO + + private var isOpen = false + private var dragOffset: Vec2d? = null + private val padding get() = ClickGui.padding + + final override val rect get() = Rect.basedOn(position, width, renderHeight + titleBarHeight) + val contentRect get() = rect.shrink(padding).moveFirst(Vec2d(0.0, titleBarHeight - padding)) + + private val titleBar get() = Rect.basedOn(rect.leftTop, rect.size.x, titleBarHeight) + private val titleBarHeight get() = titleFont.height + 2 + padding * 2 + private val titleFont: IFontEntry + + private val layer = RenderLayer() + private val animation = AnimationTicker() + + override val children = mutableListOf() + val subLayer = RenderLayer() + + private val renderHeight by animation.exp({ 0.0 }, { height + padding * 2 * isOpen.toInt() }, 0.5, ::isOpen) + + init { + // Background + layer.rect.build { + position = rect + roundRadius = ClickGui.windowRadius + color(ClickGui.backgroundColor) + } + + // Title + titleFont = layer.font.build { + text = title + position = titleBar.center - widthVec * 0.5 + } + } + + override fun onShow() { + super.onShow() + super.onShow() + + dragOffset = null + } + + override fun onHide() { + super.onHide() + } + + override fun onTick() { + animation.tick() + super.onTick() + } + + override fun onRender() { + layer.render() + + scissor(contentRect) { + subLayer.render() + super.onRender() + } + } + + override fun onMouseMove(mouse: Vec2d) { + super.onMouseMove(mouse) + super.onMouseMove(mouse) + + dragOffset?.let { + position = mouse - it + } + } + + override fun onKey(key: KeyCode) { + super.onKey(key) + } + + override fun onChar(char: Char) { + super.onChar(char) + } + + override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { + super.onMouseClick(button, action, mouse) + + dragOffset = null + + if (mouse in titleBar && action == Mouse.Action.Click) { + when(button) { + Mouse.Button.Left -> dragOffset = mouse - position + Mouse.Button.Right -> isOpen = !isOpen + } + } + + super.onMouseClick(button, action, mouse) + } + + override fun isChildAccessible(child: T) = + child.rect in contentRect +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt new file mode 100644 index 000000000..7dc3504ae --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/IComponent.kt @@ -0,0 +1,23 @@ +package com.lambda.gui.api.component.core + +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +interface IComponent { + fun onShow() {} + + fun onHide() {} + + fun onTick() {} + + fun onRender() {} + + fun onKey(key: KeyCode) {} + + fun onChar(char: Char) {} + + fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) {} + + fun onMouseMove(mouse: Vec2d) {} +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt new file mode 100644 index 000000000..cc730ce68 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt @@ -0,0 +1,54 @@ +package com.lambda.gui.api.component.core + +import com.lambda.util.KeyCode +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +interface IListComponent : IComponent { + val children: List + + fun isChildAccessible(child: T): Boolean = true + + override fun onShow() { + children.forEach(IComponent::onShow) + } + + override fun onHide() { + children.forEach(IComponent::onHide) + } + + override fun onTick() { + children.forEach(IComponent::onTick) + } + + override fun onRender() { + children.forEach(IComponent::onRender) + } + + override fun onKey(key: KeyCode) { + children.filter(::isChildAccessible).forEach { child -> + child.onKey(key) + } + } + + override fun onChar(char: Char) { + children.filter(::isChildAccessible).forEach { child -> + child.onChar(char) + } + } + + override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { + children.filter(::isChildAccessible).forEach { child -> + child.onMouseClick(button, action, mouse) + } + } + + override fun onMouseMove(mouse: Vec2d) { + children.forEach { child -> + child.onMouseMove( + if (isChildAccessible(child)) mouse + else Vec2d(-1000.0, -1000.0) // junky but worky way to unfocus + ) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt new file mode 100644 index 000000000..96bcbf26e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt @@ -0,0 +1,7 @@ +package com.lambda.gui.api.component.core + +import com.lambda.util.math.Rect + +interface IRectComponent : IComponent { + val rect: Rect +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt new file mode 100644 index 000000000..0e4520d0a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -0,0 +1,66 @@ +package com.lambda.gui.api.component.sub + +import com.lambda.graphics.animation.Animation.Companion.exp +import com.lambda.graphics.animation.AnimationTicker +import com.lambda.gui.api.component.InteractiveComponent +import com.lambda.gui.api.component.WindowComponent +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.Mouse +import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.Rect +import com.lambda.util.math.Vec2d +import java.awt.Color + +abstract class ButtonComponent(private val base: WindowComponent<*>) : InteractiveComponent() { + abstract val position: Vec2d + abstract val size: Vec2d + + abstract val text: String + open val active get() = pressed + + private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) base.contentRect.size.x else size.x, size.y) + final override val rect get() = Rect.basedOn(position, actualSize) + base.contentRect.leftTop + + private val layer = base.subLayer + private val animation = AnimationTicker() + + private val activeAnimation by animation.exp(0.0, 1.0, 0.5, ::active) + private val hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (hovered) 0.5 else 0.1 }, ::hovered) + private val pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed) + private val interactAnimation get() = lerp(hoverAnimation, 1.5, pressAnimation) * 0.4 + + init { + layer.rect.build { + position = Rect.basedOn(rect.leftTop, rect.size.x * activeAnimation, rect.size.y).shrink(interactAnimation) + color(ClickGui.mainColor) + } + + layer.rect.build { + position = rect.shrink(interactAnimation) + color(Color.WHITE.setAlpha(interactAnimation * 0.2)) + } + + layer.font.build { + text = this@ButtonComponent.text + scale = 1.0 - pressAnimation * 0.05 + + val x = rect.left + ClickGui.padding + interactAnimation + hoverAnimation * 0.5 + position = Vec2d(x, rect.center.y) + } + } + + abstract fun performClickAction(mouse: Mouse.Button) + + override fun onTick() { + animation.tick() + } + + override fun onRelease() { + if (hovered) activeMouseButton?.let(::performClickAction) + } + + companion object { + const val FILL_PARENT = -1.0 + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt similarity index 63% rename from common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt rename to common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt index 82faa3ade..ee78f7d06 100644 --- a/common/src/main/kotlin/com/lambda/gui/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt @@ -1,14 +1,14 @@ -package com.lambda.gui.layer +package com.lambda.gui.api.layer import com.lambda.graphics.renderer.IRenderer import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.rect.RectRenderer -class RenderLayer { +class RenderLayer() { private val renderers = mutableListOf>() - val rect = RectRenderer().apply(::register) - val font = FontRenderer().apply(::register) + val rect = RectRenderer().apply(::register).asRenderer + val font = FontRenderer().apply(::register).asRenderer fun register(renderer: IRenderer<*>) = renderers.add(renderer) @@ -16,4 +16,6 @@ class RenderLayer { renderers.forEach(IRenderer<*>::update) renderers.forEach(IRenderer<*>::render) } + + fun destroy() = renderers.forEach(IRenderer<*>::destroy) } diff --git a/common/src/main/kotlin/com/lambda/gui/component/Component.kt b/common/src/main/kotlin/com/lambda/gui/component/Component.kt deleted file mode 100644 index 28f5f7d06..000000000 --- a/common/src/main/kotlin/com/lambda/gui/component/Component.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.lambda.gui.component - -import com.lambda.util.KeyCode -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d - -interface IChildComponent : IComponent { - val parent: T -} - -interface IListComponent > : IComponent { - val children: List -} - -interface IRectComponent : IComponent { - val position: Pair -} - -interface IComponent { - fun onShow() - - fun onHide() - - fun onRender() - - fun onKey(key: KeyCode) - - fun onMouse(button: Mouse.Button, action: Mouse.Action) -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt deleted file mode 100644 index 79ee29f71..000000000 --- a/common/src/main/kotlin/com/lambda/gui/component/WindowComponent.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.lambda.gui.component - -import com.lambda.gui.layer.RenderLayer -import com.lambda.util.KeyCode -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d - -abstract class WindowComponent : IRectComponent { - override var position = Vec2d.ZERO to Vec2d.ZERO - - abstract val title: String - val render = RenderLayer() - - override fun onShow() { - - } - - override fun onHide() { - - } - - override fun onRender() { - - } - - override fun onKey(key: KeyCode) { - - } - - override fun onMouse(button: Mouse.Button, action: Mouse.Action) { - - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt new file mode 100644 index 000000000..1da9b33d6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -0,0 +1,47 @@ +package com.lambda.gui.impl.clickgui + +import com.lambda.gui.api.LambdaGui +import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.api.component.core.IListComponent +import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.module.ModuleRegistry +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +class LambdaClickGui : LambdaGui, IListComponent> { + override val name = "Lambda ClickGui" + override val children = mutableListOf>() + + init { + children.add(object : WindowComponent() { + override val title = "Test Window" + override var width = 110.0 + override var height = 300.0 + + init { + var buh = 0.0 + val component = this + ModuleRegistry.modules.forEach { module -> + children.add(object : ButtonComponent(component) { + override val position = Vec2d(0.0, buh) + override val size get() = Vec2d(FILL_PARENT, 11.0) + + override val text: String get() = module.name + override val active: Boolean get() = module.isEnabled + + override fun performClickAction(mouse: Mouse.Button) { + when (mouse) { + Mouse.Button.Left -> module.toggle() + Mouse.Button.Right -> { + // open settings window + } + } + } + }) + buh += 12.0 + } + } + }) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt new file mode 100644 index 000000000..fbdeea50c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -0,0 +1,45 @@ +package com.lambda.module.modules.client + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.gui.impl.clickgui.LambdaClickGui +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.threading.mainThread +import java.awt.Color + +object ClickGui : Module( + name = "ClickGui", + description = "Sexy", + defaultTags = setOf(ModuleTag.CLIENT) +) { + private val page by setting("Page", Page.Colors) + + // General + val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1) + val padding by setting("Padding", 2.0, 0.0..10.0, 0.1) + + // Colors + val mainColor by setting("Main Color", Color(110, 0, 40), visibility = { page == Page.Colors }) + val backgroundColor by setting("Background Color", Color(35, 15, 20), visibility = { page == Page.Colors }) + + enum class Page { + General, + Colors + } + + private val gui by mainThread { + LambdaClickGui() + } + + init { + onEnable { + mc.currentScreen?.close() + gui.show() + } + + listener { + if (mc.currentScreen?.title?.literalString != gui.name) disable() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/math/Rect.kt b/common/src/main/kotlin/com/lambda/util/math/Rect.kt index bcc14b84d..d7470721f 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Rect.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Rect.kt @@ -3,8 +3,6 @@ package com.lambda.util.math import com.lambda.util.math.MathUtils.lerp data class Rect(private val pos1: Vec2d, private val pos2: Vec2d) { - constructor(size: Vec2d) : this(Vec2d.ZERO, size) - val left = pos1.x val top = pos1.y val right = pos2.x @@ -24,10 +22,18 @@ data class Rect(private val pos1: Vec2d, private val pos2: Vec2d) { fun moveFirst(vec2d: Vec2d) = Rect(pos1 + vec2d, pos2) fun moveSecond(vec2d: Vec2d) = Rect(pos1, pos2 + vec2d) - fun extend(amount: Double) = Rect(pos1 - amount, pos2 + amount) - fun shrink(amount: Double) = extend(-amount) + fun expand(amount: Double) = Rect(pos1 - amount, pos2 + amount) + fun shrink(amount: Double) = expand(-amount) + + operator fun contains(point: Vec2d): Boolean { + if (size.x <= 0.0 || size.y <= 0.0) return false + return point.x in left..right && point.y in top..bottom + } - fun contains(point: Vec2d) = point.x in left..right && point.y in top..bottom + operator fun contains(other: Rect): Boolean { + if (size.x <= 0.0 || size.y <= 0.0) return false + return other.leftTop in this || other.rightBottom in this || leftTop in other || rightBottom in other + } companion object { val ZERO = Rect(Vec2d.ZERO, Vec2d.ZERO) From 6e2f721ceac57df74769ef08360aa1f69fc90f67 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 20:41:42 +0300 Subject: [PATCH 25/46] Saving the config should be the last one --- common/src/main/kotlin/com/lambda/config/Configuration.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/config/Configuration.kt b/common/src/main/kotlin/com/lambda/config/Configuration.kt index 30aeb97e3..f5ffd9fbd 100644 --- a/common/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/common/src/main/kotlin/com/lambda/config/Configuration.kt @@ -41,7 +41,7 @@ abstract class Configuration : Jsonable { init { unsafeListener { tryLoad() } - unsafeListener { trySave() } + unsafeListener(Int.MIN_VALUE) { trySave() } configurations.add(this) } From 78789f64d64d99c431bfaa2d4b148fa64090a68d Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 20:45:15 +0300 Subject: [PATCH 26/46] AbstractGui rework, refactor --- .../kotlin/com/lambda/gui/api/LambdaGui.kt | 155 ++++++++++-------- .../gui/api/component/WindowComponent.kt | 2 +- .../gui/api/component/sub/ButtonComponent.kt | 2 +- .../gui/impl/clickgui/LambdaClickGui.kt | 6 +- .../lambda/module/modules/client/ClickGui.kt | 22 ++- 5 files changed, 103 insertions(+), 84 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index 426c7c6be..c04365b8a 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -4,8 +4,9 @@ import com.lambda.Lambda.mc import com.lambda.event.EventFlow import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.event.listener.UnsafeListener import com.lambda.gui.api.component.core.IComponent +import com.lambda.module.Module import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.Nameable @@ -15,82 +16,92 @@ import net.minecraft.client.gui.DrawContext import net.minecraft.client.gui.screen.Screen import net.minecraft.text.Text -interface LambdaGui : IComponent, Nameable { - fun onCloseRequest(): Boolean = true +@Suppress("LeakingThis") +abstract class LambdaGui(override val name: String, private val owner: Module? = null) : Screen(Text.of(name)), IComponent, Nameable { + private var screenSize = Vec2d.ZERO + + private val renderListener = UnsafeListener(0, this, false) { event -> + event as RenderEvent.GUI.Scaled + screenSize = event.screenSize + onRender() + } + + private val tickListener = UnsafeListener(0, this, false) { + onTick() + } /** * Shows this gui screen * * No safe context required (TODO: let user open clickgui via main menu) */ - fun show() = recordRenderCall { - mc.setScreen(object : Screen(Text.of(name)) { - private var screenSize = Vec2d.ZERO - - private val renderListener = listener { event -> - screenSize = event.screenSize - onRender() - } - - private val tickListener = listener { - onTick() - } - - override fun onDisplayed() { - onShow() - } - - override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { - // Let's remove background tint - } - - override fun removed() { - onHide() - - with(EventFlow.syncListeners) { - unsubscribe(renderListener) - unsubscribe(tickListener) - } - } - - override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { - onKey(KeyCode(keyCode)) - - if (keyCode == KeyCode.Escape.key && onCloseRequest()) { - this.close() - } - - return true - } - - override fun charTyped(chr: Char, modifiers: Int): Boolean { - onChar(chr) - return true - } - - override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouseClick(Mouse.Button(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY)) - return true - } - - override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { - onMouseClick(Mouse.Button(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY)) - return true - } - - override fun mouseMoved(mouseX: Double, mouseY: Double) { - onMouseMove(rescaleMouse(mouseX, mouseY)) - } - - override fun shouldPause() = false - - private fun rescaleMouse(mouseX: Double, mouseY: Double): Vec2d { - val mcMouse = Vec2d(mouseX, mouseY) - val mcWindow = Vec2d(mc.window.scaledWidth, mc.window.scaledHeight) - - val uv = mcMouse / mcWindow - return uv * screenSize - } - }) + fun show() { + mc.currentScreen?.close() + + recordRenderCall { // wait for previous screen to be closed + mc.setScreen(this) + } + } + + final override fun onDisplayed() { + onShow() + + with(EventFlow.syncListeners) { + subscribe(renderListener) + subscribe(tickListener) + } + } + + final override fun removed() { + onHide() + owner?.disable() + + with(EventFlow.syncListeners) { + unsubscribe(renderListener) + unsubscribe(tickListener) + } + } + + final override fun render(context: DrawContext?, mouseX: Int, mouseY: Int, delta: Float) { + // Let's remove background tint + } + + final override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { + onKey(KeyCode(keyCode)) + + if (keyCode == KeyCode.Escape.key) { + close() + } + + return true + } + + final override fun charTyped(chr: Char, modifiers: Int): Boolean { + onChar(chr) + return true + } + + final override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouseClick(Mouse.Button(button), Mouse.Action.Click, rescaleMouse(mouseX, mouseY)) + return true + } + + final override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean { + onMouseClick(Mouse.Button(button), Mouse.Action.Release, rescaleMouse(mouseX, mouseY)) + return true + } + + final override fun mouseMoved(mouseX: Double, mouseY: Double) { + onMouseMove(rescaleMouse(mouseX, mouseY)) + } + + final override fun shouldPause() = false + + private fun rescaleMouse(mouseX: Double, mouseY: Double): Vec2d { + val mcMouse = Vec2d(mouseX, mouseY) + val mcWindow = Vec2d(mc.window.scaledWidth, mc.window.scaledHeight) + + val uv = mcMouse / mcWindow + return uv * screenSize } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 341793069..531b6c173 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -24,7 +24,7 @@ abstract class WindowComponent : InteractiveComponent(), IL private var isOpen = false private var dragOffset: Vec2d? = null - private val padding get() = ClickGui.padding + private val padding get() = ClickGui.windowPadding final override val rect get() = Rect.basedOn(position, width, renderHeight + titleBarHeight) val contentRect get() = rect.shrink(padding).moveFirst(Vec2d(0.0, titleBarHeight - padding)) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index 0e4520d0a..310dc0922 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -45,7 +45,7 @@ abstract class ButtonComponent(private val base: WindowComponent<*>) : Interacti text = this@ButtonComponent.text scale = 1.0 - pressAnimation * 0.05 - val x = rect.left + ClickGui.padding + interactAnimation + hoverAnimation * 0.5 + val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation * 0.5 position = Vec2d(x, rect.center.y) } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index 1da9b33d6..0dd7fd37b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -4,13 +4,13 @@ import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.IListComponent import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.module.Module import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -class LambdaClickGui : LambdaGui, IListComponent> { - override val name = "Lambda ClickGui" +class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { override val children = mutableListOf>() init { @@ -25,7 +25,7 @@ class LambdaClickGui : LambdaGui, IListComponent children.add(object : ButtonComponent(component) { override val position = Vec2d(0.0, buh) - override val size get() = Vec2d(FILL_PARENT, 11.0) + override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) override val text: String get() = module.name override val active: Boolean get() = module.isEnabled diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index fbdeea50c..64566eb28 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -1,7 +1,7 @@ package com.lambda.module.modules.client -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.event.events.ClientEvent +import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -17,7 +17,8 @@ object ClickGui : Module( // General val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1) - val padding by setting("Padding", 2.0, 0.0..10.0, 0.1) + val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1) + val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) // Colors val mainColor by setting("Main Color", Color(110, 0, 40), visibility = { page == Page.Colors }) @@ -34,12 +35,19 @@ object ClickGui : Module( init { onEnable { - mc.currentScreen?.close() - gui.show() + if (mc.currentScreen != gui) { + gui.show() + } } - listener { - if (mc.currentScreen?.title?.literalString != gui.name) disable() + onDisable { + if (mc.currentScreen == gui) { + gui.close() + } + } + + unsafeListener { + disable() } } } \ No newline at end of file From a936f9c531e4f0778c7ee4a43d8b3c67ea96123d Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Sun, 7 Apr 2024 23:15:23 +0300 Subject: [PATCH 27/46] Setting visibility --- .../kotlin/com/lambda/module/modules/client/ClickGui.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 64566eb28..2283b5bea 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -16,9 +16,9 @@ object ClickGui : Module( private val page by setting("Page", Page.Colors) // General - val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1) - val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1) - val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) + val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) + val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) + val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1, visibility = { page == Page.General }) // Colors val mainColor by setting("Main Color", Color(110, 0, 40), visibility = { page == Page.Colors }) From fc59c12ee121f94f071a0699dd6bcac195697486 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Mon, 8 Apr 2024 23:14:13 +0300 Subject: [PATCH 28/46] Better animations and code structure --- .../kotlin/com/lambda/graphics/gl/Scissor.kt | 31 +++----- .../kotlin/com/lambda/gui/api/LambdaGui.kt | 4 + .../gui/api/component/InteractiveComponent.kt | 3 +- .../gui/api/component/WindowComponent.kt | 20 +++-- .../gui/api/component/core/IRectComponent.kt | 2 +- .../api/component/core/list/ChildComponent.kt | 14 ++++ .../component/core/list/IChildComponent.kt | 7 ++ .../core/{ => list}/IListComponent.kt | 3 +- .../gui/api/component/sub/ButtonComponent.kt | 76 +++++++++++++++---- .../com/lambda/gui/api/layer/RenderLayer.kt | 29 ++++--- .../gui/impl/clickgui/LambdaClickGui.kt | 38 ++-------- .../gui/impl/clickgui/buttons/ModuleButton.kt | 27 +++++++ .../gui/impl/clickgui/windows/TagWindow.kt | 31 ++++++++ .../lambda/module/modules/client/ClickGui.kt | 6 +- .../kotlin/com/lambda/util/math/MathUtils.kt | 6 ++ .../main/kotlin/com/lambda/util/math/Rect.kt | 6 ++ 16 files changed, 213 insertions(+), 90 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt rename common/src/main/kotlin/com/lambda/gui/api/component/core/{ => list}/IListComponent.kt (93%) create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt index d7afae594..b2bcac907 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt @@ -5,30 +5,21 @@ import com.lambda.module.modules.client.HUD import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.Rect -import com.lambda.util.math.Vec2d import com.mojang.blaze3d.systems.RenderSystem.disableScissor import com.mojang.blaze3d.systems.RenderSystem.enableScissor import kotlin.math.max -import kotlin.math.min object Scissor { - private var stack = ArrayDeque() + private var stack = ArrayDeque() - fun scissor(rect: Rect, block: () -> Unit) = scissor(rect.leftTop, rect.rightBottom, block) - - fun scissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) { - stack.lastOrNull()?.let { - registerScissor( - // clamp corners so children scissor box can't overlap parent - Vec2d(max(pos1.x, it.pos1.x), max(pos1.y, it.pos1.y)), - Vec2d(min(pos2.x, it.pos2.x), min(pos2.y, it.pos2.y)), - block - ) - } ?: registerScissor(pos1, pos2, block) + fun scissor(rect: Rect, block: () -> Unit) { + // clamp corners so children scissor box can't overlap parent + val processed = stack.lastOrNull()?.let { rect.clamp(it) } ?: rect + registerScissor(processed, block) } - private fun registerScissor(pos1: Vec2d, pos2: Vec2d, block: () -> Unit) { - scissor(Entry(pos1, pos2)) + private fun registerScissor(rect: Rect, block: () -> Unit) { + scissor(rect) block() @@ -36,7 +27,7 @@ object Scissor { stack.lastOrNull().apply(::scissor) } - private fun scissor(entry: Entry?) { + private fun scissor(entry: Rect?) { if (entry == null) { disableScissor() return @@ -44,8 +35,8 @@ object Scissor { stack.add(entry) - val pos1 = entry.pos1 * HUD.scale - val pos2 = entry.pos2 * HUD.scale + val pos1 = entry.leftTop * HUD.scale + val pos2 = entry.rightBottom * HUD.scale val width = max(pos2.x - pos1.x, 0.0) val height = max(pos2.y - pos1.y, 0.0) @@ -59,6 +50,4 @@ object Scissor { height.ceilToInt() ) } - - private data class Entry(val pos1: Vec2d, val pos2: Vec2d) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index c04365b8a..82ca29565 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -54,7 +54,11 @@ abstract class LambdaGui(override val name: String, private val owner: Module? = final override fun removed() { onHide() + + // quick crashfix (is there any other way to prevent gui being closed twice?) + mc.currentScreen = null owner?.disable() + mc.currentScreen = this with(EventFlow.syncListeners) { unsubscribe(renderListener) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt index 9519edbb8..5fbb85424 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt @@ -1,10 +1,11 @@ package com.lambda.gui.api.component +import com.lambda.gui.api.component.core.IComponent import com.lambda.gui.api.component.core.IRectComponent import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -abstract class InteractiveComponent : IRectComponent { +abstract class InteractiveComponent : IComponent, IRectComponent { protected var hovered = false protected var pressed = false; set(value) { if (field == value) return diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 531b6c173..f44d7e5df 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -4,8 +4,9 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.animation.AnimationTicker import com.lambda.graphics.gl.Scissor.scissor import com.lambda.graphics.renderer.gui.font.IFontEntry -import com.lambda.gui.api.component.core.IListComponent -import com.lambda.gui.api.component.core.IRectComponent +import com.lambda.gui.api.component.core.IComponent +import com.lambda.gui.api.component.core.list.IListComponent +import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.gui.api.layer.RenderLayer import com.lambda.module.modules.client.ClickGui import com.lambda.util.KeyCode @@ -14,7 +15,7 @@ import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d -abstract class WindowComponent : InteractiveComponent(), IListComponent { +abstract class WindowComponent : InteractiveComponent(), IListComponent { abstract val title: String abstract var width: Double @@ -22,7 +23,7 @@ abstract class WindowComponent : InteractiveComponent(), IL var position = Vec2d.ZERO - private var isOpen = false + private var isOpen = true private var dragOffset: Vec2d? = null private val padding get() = ClickGui.windowPadding @@ -37,7 +38,7 @@ abstract class WindowComponent : InteractiveComponent(), IL private val animation = AnimationTicker() override val children = mutableListOf() - val subLayer = RenderLayer() + val subLayer = RenderLayer(true) private val renderHeight by animation.exp({ 0.0 }, { height + padding * 2 * isOpen.toInt() }, 0.5, ::isOpen) @@ -69,7 +70,14 @@ abstract class WindowComponent : InteractiveComponent(), IL override fun onTick() { animation.tick() - super.onTick() + + children.forEach { child -> + child.visible = isChildAccessible(child) + } + + children + .filter(ChildComponent::visible) + .forEach(IComponent::onTick) } override fun onRender() { diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt index 96bcbf26e..0843a1ccb 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/IRectComponent.kt @@ -2,6 +2,6 @@ package com.lambda.gui.api.component.core import com.lambda.util.math.Rect -interface IRectComponent : IComponent { +interface IRectComponent { val rect: Rect } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt new file mode 100644 index 000000000..494c4ae93 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt @@ -0,0 +1,14 @@ +package com.lambda.gui.api.component.core.list + +import com.lambda.gui.api.component.InteractiveComponent + +abstract class ChildComponent : InteractiveComponent(), IChildComponent { + // mostly used to create an animation when an element appears + var visible = false; set(value) { + if (field == value) return + field = value + + if (value) onShow() + else onHide() + } +} diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt new file mode 100644 index 000000000..353ab501f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IChildComponent.kt @@ -0,0 +1,7 @@ +package com.lambda.gui.api.component.core.list + +import com.lambda.gui.api.component.core.IComponent + +interface IChildComponent { + val owner: IComponent +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt similarity index 93% rename from common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt rename to common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt index cc730ce68..dd8b2f60d 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/IListComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt @@ -1,5 +1,6 @@ -package com.lambda.gui.api.component.core +package com.lambda.gui.api.component.core.list +import com.lambda.gui.api.component.core.IComponent import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.math.Vec2d diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index 310dc0922..c1d32c6ca 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -2,56 +2,95 @@ package com.lambda.gui.api.component.sub import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.animation.AnimationTicker -import com.lambda.gui.api.component.InteractiveComponent import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse -import com.lambda.util.math.ColorUtils.setAlpha +import com.lambda.util.math.ColorUtils.multAlpha import com.lambda.util.math.MathUtils.lerp +import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d import java.awt.Color +import kotlin.math.abs -abstract class ButtonComponent(private val base: WindowComponent<*>) : InteractiveComponent() { +abstract class ButtonComponent(final override val owner: WindowComponent<*>) : ChildComponent() { abstract val position: Vec2d abstract val size: Vec2d abstract val text: String open val active get() = pressed - private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) base.contentRect.size.x else size.x, size.y) - final override val rect get() = Rect.basedOn(position, actualSize) + base.contentRect.leftTop + private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.contentRect.size.x else size.x, size.y) + final override val rect get() = Rect.basedOn(position, actualSize) + owner.contentRect.leftTop - private val layer = base.subLayer + private val layer = owner.subLayer private val animation = AnimationTicker() - private val activeAnimation by animation.exp(0.0, 1.0, 0.5, ::active) - private val hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (hovered) 0.5 else 0.1 }, ::hovered) - private val pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed) + private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active) + private var hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered || active) 0.5 else 0.1 }, ::renderHovered) + private var pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed) private val interactAnimation get() = lerp(hoverAnimation, 1.5, pressAnimation) * 0.4 + private var lastHoveredTime = 0L + private val renderHovered get() = hovered || + System.currentTimeMillis() - lastHoveredTime < 110 // a bit more than 2 ticks + init { + // Active color layer.rect.build { - position = Rect.basedOn(rect.leftTop, rect.size.x * activeAnimation, rect.size.y).shrink(interactAnimation) - color(ClickGui.mainColor) + position = rect.shrink(interactAnimation) + color(ClickGui.mainColor.multAlpha(activeAnimation * 0.2)) } + // Hover glint layer.rect.build { - position = rect.shrink(interactAnimation) - color(Color.WHITE.setAlpha(interactAnimation * 0.2)) + val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverAnimation, rect.size.y) + position = hoverRect.shrink(interactAnimation) + + val alpha = interactAnimation * 0.3 + color(ClickGui.mainColor.multAlpha(alpha)) } + // Toggle fx + layer.rect.build { + val left = rect - Vec2d(rect.size.x, 0.0) + val right = rect + Vec2d(rect.size.x, 0.0) + + position = lerp(left, right, activeAnimation) + .clamp(rect) + .shrink(interactAnimation) + + // 0.0 .. 1.0 .. 0.0 animation + val alpha = 1.0 - (abs(activeAnimation - 0.5) * 2.0) + val color = ClickGui.mainColor.multAlpha(alpha * 0.8) + + // "Tail" effect + val leftColor = color.multAlpha(1.0 - active.toInt()) + val rightColor = color.multAlpha(active.toInt().toDouble()) + + colorH(leftColor, rightColor) + } + + // Text layer.font.build { text = this@ButtonComponent.text - scale = 1.0 - pressAnimation * 0.05 + scale = 1.0 - pressAnimation * 0.08 - val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation * 0.5 + color = lerp(Color.WHITE, ClickGui.mainColor, activeAnimation) + + val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation position = Vec2d(x, rect.center.y) } } abstract fun performClickAction(mouse: Mouse.Button) + override fun onShow() { + super.onShow() + activeAnimation = 0.0 + } + override fun onTick() { animation.tick() } @@ -60,6 +99,13 @@ abstract class ButtonComponent(private val base: WindowComponent<*>) : Interacti if (hovered) activeMouseButton?.let(::performClickAction) } + override fun onMouseMove(mouse: Vec2d) { + super.onMouseMove(mouse) + + val time = System.currentTimeMillis() + if (hovered) lastHoveredTime = time + } + companion object { const val FILL_PARENT = -1.0 } diff --git a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt index ee78f7d06..0a8b6e026 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt @@ -1,21 +1,30 @@ package com.lambda.gui.api.layer -import com.lambda.graphics.renderer.IRenderer import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.rect.RectRenderer +import com.lambda.module.modules.client.ClickGui +import com.mojang.blaze3d.systems.RenderSystem.blendFunc +import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc +import org.lwjgl.opengl.GL11.GL_ONE +import org.lwjgl.opengl.GL11.GL_SRC_ALPHA -class RenderLayer() { - private val renderers = mutableListOf>() +class RenderLayer(private val allowGlowing: Boolean = false) { + val rect = RectRenderer().asRenderer + val font = FontRenderer().asRenderer - val rect = RectRenderer().apply(::register).asRenderer - val font = FontRenderer().apply(::register).asRenderer + fun render() { + rect.update() + font.update() - fun register(renderer: IRenderer<*>) = renderers.add(renderer) + if (allowGlowing && ClickGui.glow) blendFunc(GL_SRC_ALPHA, GL_ONE) + rect.render() + defaultBlendFunc() - fun render() { - renderers.forEach(IRenderer<*>::update) - renderers.forEach(IRenderer<*>::render) + font.render() } - fun destroy() = renderers.forEach(IRenderer<*>::destroy) + fun destroy() { + rect.destroy() + font.destroy() + } } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index 0dd7fd37b..e94c1c013 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -2,46 +2,18 @@ package com.lambda.gui.impl.clickgui import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent -import com.lambda.gui.api.component.core.IListComponent +import com.lambda.gui.api.component.core.list.IListComponent import com.lambda.gui.api.component.sub.ButtonComponent -import com.lambda.module.Module +import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { - override val children = mutableListOf>() +class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { + override val children = mutableListOf>() init { - children.add(object : WindowComponent() { - override val title = "Test Window" - override var width = 110.0 - override var height = 300.0 - - init { - var buh = 0.0 - val component = this - ModuleRegistry.modules.forEach { module -> - children.add(object : ButtonComponent(component) { - override val position = Vec2d(0.0, buh) - override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) - - override val text: String get() = module.name - override val active: Boolean get() = module.isEnabled - - override fun performClickAction(mouse: Mouse.Button) { - when (mouse) { - Mouse.Button.Left -> module.toggle() - Mouse.Button.Right -> { - // open settings window - } - } - } - }) - buh += 12.0 - } - } - }) + children.add(TagWindow()) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt new file mode 100644 index 000000000..3649b06ec --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt @@ -0,0 +1,27 @@ +package com.lambda.gui.impl.clickgui.buttons + +import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.module.Module +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d + +class ModuleButton(val module: Module, owner: WindowComponent<*>) : ButtonComponent(owner) { + override val position get() = Vec2d(0.0, heightOffset) + override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) + + override val text: String get() = module.name + override val active: Boolean get() = module.isEnabled + + var heightOffset = 0.0 + + override fun performClickAction(mouse: Mouse.Button) { + when (mouse) { + Mouse.Button.Left -> module.toggle() + Mouse.Button.Right -> { + // open settings window + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt new file mode 100644 index 000000000..4506443f3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -0,0 +1,31 @@ +package com.lambda.gui.impl.clickgui.windows + +import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.module.ModuleRegistry +import com.lambda.module.modules.client.ClickGui + +class TagWindow : WindowComponent() { + override val title = "Test Window" + override var width = 110.0 + override var height = 300.0 + + init { + ModuleRegistry.modules.forEach { + children.add(ModuleButton(it, this)) + } + } + + override fun onRender() { + updateModules() + super.onRender() + } + + private fun updateModules() { + children.sortBy { it.module.name } + + children.forEachIndexed { i, button -> + button.heightOffset = i * (ClickGui.buttonHeight + ClickGui.buttonStep) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 2283b5bea..c07e1585d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -19,10 +19,12 @@ object ClickGui : Module( val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1, visibility = { page == Page.General }) + val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1, visibility = { page == Page.General }) // Colors - val mainColor by setting("Main Color", Color(110, 0, 40), visibility = { page == Page.Colors }) - val backgroundColor by setting("Background Color", Color(35, 15, 20), visibility = { page == Page.Colors }) + val mainColor by setting("Main Color", Color(100, 215, 255), visibility = { page == Page.Colors }) + val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) + val glow by setting("Glow (experimental)", true, visibility = { page == Page.Colors }) enum class Page { General, diff --git a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt index 08c2f32d6..94abd79f5 100644 --- a/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/MathUtils.kt @@ -125,6 +125,12 @@ object MathUtils { lerp(start.y, end.y, factor) ) + fun lerp(start: Rect, end: Rect, factor: Double) = + Rect( + lerp(start.leftTop, end.leftTop, factor), + lerp(start.rightBottom, end.rightBottom, factor) + ) + fun lerp(start: Rotation, end: Rotation, factor: Double) = Rotation( lerp(start.yaw, end.yaw, factor), diff --git a/common/src/main/kotlin/com/lambda/util/math/Rect.kt b/common/src/main/kotlin/com/lambda/util/math/Rect.kt index d7470721f..8fcfe756c 100644 --- a/common/src/main/kotlin/com/lambda/util/math/Rect.kt +++ b/common/src/main/kotlin/com/lambda/util/math/Rect.kt @@ -25,6 +25,12 @@ data class Rect(private val pos1: Vec2d, private val pos2: Vec2d) { fun expand(amount: Double) = Rect(pos1 - amount, pos2 + amount) fun shrink(amount: Double) = expand(-amount) + fun clamp(rect: Rect) = + Rect( + Vec2d(max(left, rect.left), max(top, rect.top)), + Vec2d(min(right, rect.right), min(bottom, rect.bottom)) + ) + operator fun contains(point: Vec2d): Boolean { if (size.x <= 0.0 || size.y <= 0.0) return false return point.x in left..right && point.y in top..bottom From 14fbebce7e6b75651bf7218947d8dde156cc8caf Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 9 Apr 2024 02:44:11 +0200 Subject: [PATCH 29/46] Allow using Keybinds in GUI screen --- common/src/main/kotlin/com/lambda/module/Module.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 7adf79675..f5a1c607b 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -13,6 +13,7 @@ import com.lambda.event.listener.Listener import com.lambda.event.listener.SafeListener import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.event.listener.UnsafeListener +import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode import com.lambda.util.Nameable @@ -106,9 +107,11 @@ abstract class Module( init { listener(alwaysListen = true) { event -> - if (mc.currentScreen == null && event.key == keybind.key) { - toggle() - } + val screen = mc.currentScreen + if (event.key == keybind.key + && (screen == null + || screen is LambdaClickGui) + ) toggle() } } From 08545d77d4e5cbe62d4f362762b82ef594401889 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Tue, 9 Apr 2024 21:09:42 +0300 Subject: [PATCH 30/46] Let various elements access their parents --- .../lambda/gui/api/component/WindowComponent.kt | 4 +++- .../api/component/core/list/IListComponent.kt | 2 +- .../gui/api/component/sub/ButtonComponent.kt | 4 ++-- .../lambda/gui/impl/clickgui/LambdaClickGui.kt | 17 ++++++++++------- .../gui/impl/clickgui/buttons/ModuleButton.kt | 4 ++-- .../gui/impl/clickgui/windows/TagWindow.kt | 12 ++++-------- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index f44d7e5df..8ad394255 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -4,9 +4,11 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.animation.AnimationTicker import com.lambda.graphics.gl.Scissor.scissor import com.lambda.graphics.renderer.gui.font.IFontEntry +import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.core.IComponent import com.lambda.gui.api.component.core.list.IListComponent import com.lambda.gui.api.component.core.list.ChildComponent +import com.lambda.gui.api.component.core.list.IChildComponent import com.lambda.gui.api.layer.RenderLayer import com.lambda.module.modules.client.ClickGui import com.lambda.util.KeyCode @@ -15,7 +17,7 @@ import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d -abstract class WindowComponent : InteractiveComponent(), IListComponent { +abstract class WindowComponent (override val owner: LambdaGui) : InteractiveComponent(), IListComponent, IChildComponent { abstract val title: String abstract var width: Double diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt index dd8b2f60d..995bfa988 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt @@ -6,7 +6,7 @@ import com.lambda.util.Mouse import com.lambda.util.math.Vec2d interface IListComponent : IComponent { - val children: List + val children: MutableList fun isChildAccessible(child: T): Boolean = true diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index c1d32c6ca..7d22f2602 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -14,7 +14,7 @@ import com.lambda.util.math.Vec2d import java.awt.Color import kotlin.math.abs -abstract class ButtonComponent(final override val owner: WindowComponent<*>) : ChildComponent() { +abstract class ButtonComponent(override val owner: WindowComponent<*>) : ChildComponent() { abstract val position: Vec2d abstract val size: Vec2d @@ -24,7 +24,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.contentRect.size.x else size.x, size.y) final override val rect get() = Rect.basedOn(position, actualSize) + owner.contentRect.leftTop - private val layer = owner.subLayer + private val layer by lazy { owner.subLayer } private val animation = AnimationTicker() private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index e94c1c013..c8b1a0f46 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -1,19 +1,22 @@ package com.lambda.gui.impl.clickgui import com.lambda.gui.api.LambdaGui -import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.IListComponent -import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d -class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { - override val children = mutableListOf>() +class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent { + override val children = mutableListOf() init { - children.add(TagWindow()) + TagWindow(this).apply { + children.addAll( + ModuleRegistry.modules.map { + ModuleButton(it, this) + } + ) + }.apply(children::add) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt index 3649b06ec..7803d7fec 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt @@ -1,13 +1,13 @@ package com.lambda.gui.impl.clickgui.buttons -import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.sub.ButtonComponent +import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.Module import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -class ModuleButton(val module: Module, owner: WindowComponent<*>) : ButtonComponent(owner) { +class ModuleButton(val module: Module, override val owner: TagWindow) : ButtonComponent(owner) { override val position get() = Vec2d(0.0, heightOffset) override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 4506443f3..2641d2e23 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -1,21 +1,17 @@ package com.lambda.gui.impl.clickgui.windows +import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.impl.clickgui.buttons.ModuleButton -import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui -class TagWindow : WindowComponent() { +class TagWindow(override val owner: LambdaGui) : WindowComponent(owner) { override val title = "Test Window" + + // TODO: resizing override var width = 110.0 override var height = 300.0 - init { - ModuleRegistry.modules.forEach { - children.add(ModuleButton(it, this)) - } - } - override fun onRender() { updateModules() super.onRender() From c7464f3203fa5e1a09fa16801b721d53cd691697 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Tue, 9 Apr 2024 22:03:44 +0300 Subject: [PATCH 31/46] Revert "Let various elements access their parents" This reverts commit 08545d77d4e5cbe62d4f362762b82ef594401889. --- .../lambda/gui/api/component/WindowComponent.kt | 4 +--- .../api/component/core/list/IListComponent.kt | 2 +- .../gui/api/component/sub/ButtonComponent.kt | 4 ++-- .../lambda/gui/impl/clickgui/LambdaClickGui.kt | 17 +++++++---------- .../gui/impl/clickgui/buttons/ModuleButton.kt | 4 ++-- .../gui/impl/clickgui/windows/TagWindow.kt | 12 ++++++++---- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 8ad394255..f44d7e5df 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -4,11 +4,9 @@ import com.lambda.graphics.animation.Animation.Companion.exp import com.lambda.graphics.animation.AnimationTicker import com.lambda.graphics.gl.Scissor.scissor import com.lambda.graphics.renderer.gui.font.IFontEntry -import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.core.IComponent import com.lambda.gui.api.component.core.list.IListComponent import com.lambda.gui.api.component.core.list.ChildComponent -import com.lambda.gui.api.component.core.list.IChildComponent import com.lambda.gui.api.layer.RenderLayer import com.lambda.module.modules.client.ClickGui import com.lambda.util.KeyCode @@ -17,7 +15,7 @@ import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d -abstract class WindowComponent (override val owner: LambdaGui) : InteractiveComponent(), IListComponent, IChildComponent { +abstract class WindowComponent : InteractiveComponent(), IListComponent { abstract val title: String abstract var width: Double diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt index 995bfa988..dd8b2f60d 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt @@ -6,7 +6,7 @@ import com.lambda.util.Mouse import com.lambda.util.math.Vec2d interface IListComponent : IComponent { - val children: MutableList + val children: List fun isChildAccessible(child: T): Boolean = true diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index 7d22f2602..c1d32c6ca 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -14,7 +14,7 @@ import com.lambda.util.math.Vec2d import java.awt.Color import kotlin.math.abs -abstract class ButtonComponent(override val owner: WindowComponent<*>) : ChildComponent() { +abstract class ButtonComponent(final override val owner: WindowComponent<*>) : ChildComponent() { abstract val position: Vec2d abstract val size: Vec2d @@ -24,7 +24,7 @@ abstract class ButtonComponent(override val owner: WindowComponent<*>) : ChildCo private val actualSize get() = Vec2d(if (size.x == FILL_PARENT) owner.contentRect.size.x else size.x, size.y) final override val rect get() = Rect.basedOn(position, actualSize) + owner.contentRect.leftTop - private val layer by lazy { owner.subLayer } + private val layer = owner.subLayer private val animation = AnimationTicker() private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index c8b1a0f46..e94c1c013 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -1,22 +1,19 @@ package com.lambda.gui.impl.clickgui import com.lambda.gui.api.LambdaGui +import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.IListComponent -import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.gui.api.component.sub.ButtonComponent import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui +import com.lambda.util.Mouse +import com.lambda.util.math.Vec2d -class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent { - override val children = mutableListOf() +class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { + override val children = mutableListOf>() init { - TagWindow(this).apply { - children.addAll( - ModuleRegistry.modules.map { - ModuleButton(it, this) - } - ) - }.apply(children::add) + children.add(TagWindow()) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt index 7803d7fec..3649b06ec 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/buttons/ModuleButton.kt @@ -1,13 +1,13 @@ package com.lambda.gui.impl.clickgui.buttons +import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.sub.ButtonComponent -import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.Module import com.lambda.module.modules.client.ClickGui import com.lambda.util.Mouse import com.lambda.util.math.Vec2d -class ModuleButton(val module: Module, override val owner: TagWindow) : ButtonComponent(owner) { +class ModuleButton(val module: Module, owner: WindowComponent<*>) : ButtonComponent(owner) { override val position get() = Vec2d(0.0, heightOffset) override val size get() = Vec2d(FILL_PARENT, ClickGui.buttonHeight) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 2641d2e23..4506443f3 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -1,17 +1,21 @@ package com.lambda.gui.impl.clickgui.windows -import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.impl.clickgui.buttons.ModuleButton +import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui -class TagWindow(override val owner: LambdaGui) : WindowComponent(owner) { +class TagWindow : WindowComponent() { override val title = "Test Window" - - // TODO: resizing override var width = 110.0 override var height = 300.0 + init { + ModuleRegistry.modules.forEach { + children.add(ModuleButton(it, this)) + } + } + override fun onRender() { updateModules() super.onRender() From 9efcebcacdd4bfd54a60dcf7d8654eb10345ce54 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 10 Apr 2024 01:01:12 +0300 Subject: [PATCH 32/46] Color waving --- .../graphics/renderer/gui/rect/RectEntry.kt | 2 -- .../renderer/gui/rect/RectRenderer.kt | 20 ++++++++++++++++ .../com/lambda/gui/api/layer/RenderLayer.kt | 19 +++++++++++---- .../lambda/module/modules/client/ClickGui.kt | 14 +++++++++-- .../shaders/fragment/renderer/rect.frag | 24 ++++++++++++++++--- .../lambda/shaders/vertex/renderer/rect.vert | 2 ++ 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt index 29c184d27..085aaf4eb 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectEntry.kt @@ -11,7 +11,6 @@ class RectEntry( override val updateBlock: IRectEntry.() -> Unit ) : IRectEntry { override var position by owner.field(Rect.ZERO) - override var roundRadius by owner.field(0.0) private var leftTop by owner.field(Color.WHITE!!) @@ -65,7 +64,6 @@ class RectEntry( interface IRectEntry : IRenderEntry { var position: Rect - var roundRadius: Double fun color(leftTop: Color, rightTop: Color, rightBottom: Color, leftBottom: Color) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt index 10f44231d..e670eea62 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt @@ -3,15 +3,35 @@ package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader +import com.lambda.module.modules.client.ClickGui +import com.lambda.util.math.Vec2d +import org.lwjgl.glfw.GLFW.glfwGetTime class RectRenderer : AbstractGuiRenderer( VertexAttrib.Group.RECT ) { + var shadeColor = false + override fun render() { shader.use() + + shader["u_Shade"] = shadeColor + + if (shadeColor) { + shader["u_Time"] = glfwGetTime() * ClickGui.colorSpeed * 3.0 + shader["u_Color1"] = ClickGui.shadeColor1 + shader["u_Color2"] = ClickGui.shadeColor2 + shader["u_Size"] = Vec2d.ONE / Vec2d(ClickGui.colorWidth, ClickGui.colorHeight) + } + super.render() } + override fun build(block: IRectEntry.() -> Unit): IRectEntry { + shadeColor = false + return super.build(block) + } + override fun newEntry(block: IRectEntry.() -> Unit) = RectEntry(this, block) diff --git a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt index 0a8b6e026..d892ed121 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt @@ -8,17 +8,18 @@ import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc import org.lwjgl.opengl.GL11.GL_ONE import org.lwjgl.opengl.GL11.GL_SRC_ALPHA -class RenderLayer(private val allowGlowing: Boolean = false) { - val rect = RectRenderer().asRenderer +class RenderLayer(private val allowEffects: Boolean = false) { + private val rectRenderer = RectRenderer() + + val rect = rectRenderer.asRenderer val font = FontRenderer().asRenderer fun render() { rect.update() font.update() - if (allowGlowing && ClickGui.glow) blendFunc(GL_SRC_ALPHA, GL_ONE) - rect.render() - defaultBlendFunc() + rectRenderer.shadeColor = ClickGui.shade + applyFancyBlending(rect::render) font.render() } @@ -27,4 +28,12 @@ class RenderLayer(private val allowGlowing: Boolean = false) { rect.destroy() font.destroy() } + + private fun applyFancyBlending(block: () -> Unit) { + if (ClickGui.glow && allowEffects) { + blendFunc(GL_SRC_ALPHA, GL_ONE) + block() + defaultBlendFunc() + } else block() + } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index c07e1585d..2ca79db87 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -22,9 +22,19 @@ object ClickGui : Module( val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1, visibility = { page == Page.General }) // Colors - val mainColor by setting("Main Color", Color(100, 215, 255), visibility = { page == Page.Colors }) + private val primaryColor by setting("Primary Color", Color(130, 200, 255), visibility = { page == Page.Colors }) + private val secondaryColor by setting("Secondary Color", Color(225, 130, 225), visibility = { page == Page.Colors && shade }) val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) - val glow by setting("Glow (experimental)", true, visibility = { page == Page.Colors }) + val glow by setting("Glow", true, visibility = { page == Page.Colors }) + val shade by setting("Shade Color", true, visibility = { page == Page.Colors }) + val colorWidth by setting("Color Width", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) + val colorHeight by setting("Color Height", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) + val colorSpeed by setting("Color Speed", 1.0, 0.1..10.0, 0.1, visibility = { page == Page.Colors && shade }) + + val mainColor: Color get() = if (shade) Color.WHITE else primaryColor + + val shadeColor1 get() = primaryColor + val shadeColor2 get() = secondaryColor enum class Page { General, diff --git a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag index c01cac634..08a07c8be 100644 --- a/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag +++ b/common/src/main/resources/assets/lambda/shaders/fragment/renderer/rect.frag @@ -1,5 +1,12 @@ #version 330 core +uniform bool u_Shade; +uniform float u_Time; +uniform vec4 u_Color1; +uniform vec4 u_Color2; +uniform vec2 u_Size; + +in vec2 v_Position; in vec2 v_TexCoord; in vec4 v_Color; in vec2 v_Size; @@ -9,7 +16,16 @@ out vec4 color; #define SMOOTHING 0.5 -void main() { +vec4 shade() { + if (!u_Shade) return v_Color; + + vec2 pos = v_Position * u_Size; + float p = sin(pos.x + pos.y - u_Time) * 0.5 + 0.5; + + return mix(u_Color1, u_Color2, p) * vec4(1.0, 1.0, 1.0, v_Color.a); +} + +vec4 round() { vec2 halfSize = v_Size * 0.5; float radius = max(v_RoundRadius, SMOOTHING); @@ -21,7 +37,9 @@ void main() { float distance = length(max(abs(center) - halfSize + radius, 0.0)) - radius; float alpha = 1.0 - smoothstep(-SMOOTHING, SMOOTHING, distance); - alpha = clamp(alpha, 0.0, 1.0); + return vec4(1.0, 1.0, 1.0, clamp(alpha, 0.0, 1.0)); +} - color = v_Color * vec4(1.0, 1.0, 1.0, alpha); +void main() { + color = shade() * round(); } \ No newline at end of file diff --git a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert index 6a6a65fab..39940d4b9 100644 --- a/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert +++ b/common/src/main/resources/assets/lambda/shaders/vertex/renderer/rect.vert @@ -8,6 +8,7 @@ layout (location = 3) in vec4 color; uniform mat4 u_Projection; uniform mat4 u_ModelView; +out vec2 v_Position; out vec2 v_TexCoord; out vec4 v_Color; out vec2 v_Size; @@ -16,6 +17,7 @@ out float v_RoundRadius; void main() { gl_Position = u_Projection * u_ModelView * pos; + v_Position = pos.xy; v_TexCoord = uv; v_Color = color; From 15853264af31b0d016c7c8f7150b95a6fb7ee765 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 10 Apr 2024 15:14:21 +0300 Subject: [PATCH 33/46] Cleanup(merged settings, rect renderging features) --- .../kotlin/com/lambda/graphics/RenderMain.kt | 6 +-- .../kotlin/com/lambda/graphics/gl/Scissor.kt | 6 +-- .../renderer/gui/rect/RectRenderer.kt | 17 ++++++--- .../gui/api/component/WindowComponent.kt | 3 +- .../gui/api/component/sub/ButtonComponent.kt | 9 +++-- .../com/lambda/gui/api/layer/RenderLayer.kt | 21 +++-------- .../lambda/module/modules/client/ClickGui.kt | 30 ++------------- .../module/modules/client/GuiSettings.kt | 37 +++++++++++++++++++ .../com/lambda/module/modules/client/HUD.kt | 12 ------ .../kotlin/com/lambda/util/text/TextDsl.kt | 3 -- 10 files changed, 72 insertions(+), 72 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt index 3ec5d7641..c3a935af3 100644 --- a/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt +++ b/common/src/main/kotlin/com/lambda/graphics/RenderMain.kt @@ -7,7 +7,7 @@ import com.lambda.graphics.gl.GlStateUtils.setupGL import com.lambda.graphics.gl.Matrices import com.lambda.graphics.gl.Matrices.resetMatrix import com.lambda.graphics.gl.Matrices.translate -import com.lambda.module.modules.client.HUD +import com.lambda.module.modules.client.GuiSettings import org.joml.Matrix4f object RenderMain { @@ -20,8 +20,8 @@ object RenderMain { translate(0.0, 0.0, -3000.0) setupGL { - rescale(HUD.scale) - RenderEvent.GUI.Scaled(HUD.scale).post() + rescale(GuiSettings.scale) + RenderEvent.GUI.Scaled(GuiSettings.scale).post() rescale(1.0) RenderEvent.GUI.Fixed().post() diff --git a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt index b2bcac907..248975925 100644 --- a/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt +++ b/common/src/main/kotlin/com/lambda/graphics/gl/Scissor.kt @@ -1,7 +1,7 @@ package com.lambda.graphics.gl import com.lambda.Lambda.mc -import com.lambda.module.modules.client.HUD +import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.MathUtils.ceilToInt import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.Rect @@ -35,8 +35,8 @@ object Scissor { stack.add(entry) - val pos1 = entry.leftTop * HUD.scale - val pos2 = entry.rightBottom * HUD.scale + val pos1 = entry.leftTop * GuiSettings.scale + val pos2 = entry.rightBottom * GuiSettings.scale val width = max(pos2.x - pos1.x, 0.0) val height = max(pos2.y - pos1.y, 0.0) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt index e670eea62..5ba178377 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/gui/rect/RectRenderer.kt @@ -3,14 +3,19 @@ package com.lambda.graphics.renderer.gui.rect import com.lambda.graphics.buffer.vao.vertex.VertexAttrib import com.lambda.graphics.renderer.gui.AbstractGuiRenderer import com.lambda.graphics.shader.Shader -import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings import com.lambda.util.math.Vec2d +import com.mojang.blaze3d.systems.RenderSystem.blendFunc +import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc import org.lwjgl.glfw.GLFW.glfwGetTime +import org.lwjgl.opengl.GL11.GL_ONE +import org.lwjgl.opengl.GL11.GL_SRC_ALPHA class RectRenderer : AbstractGuiRenderer( VertexAttrib.Group.RECT ) { var shadeColor = false + var fancyBlending = false override fun render() { shader.use() @@ -18,13 +23,15 @@ class RectRenderer : AbstractGuiRenderer( shader["u_Shade"] = shadeColor if (shadeColor) { - shader["u_Time"] = glfwGetTime() * ClickGui.colorSpeed * 3.0 - shader["u_Color1"] = ClickGui.shadeColor1 - shader["u_Color2"] = ClickGui.shadeColor2 - shader["u_Size"] = Vec2d.ONE / Vec2d(ClickGui.colorWidth, ClickGui.colorHeight) + shader["u_Time"] = glfwGetTime() * GuiSettings.colorSpeed * 5.0 + shader["u_Color1"] = GuiSettings.shadeColor1 + shader["u_Color2"] = GuiSettings.shadeColor2 + shader["u_Size"] = Vec2d.ONE / Vec2d(GuiSettings.colorWidth, GuiSettings.colorHeight) } + if (fancyBlending) blendFunc(GL_SRC_ALPHA, GL_ONE) super.render() + defaultBlendFunc() } override fun build(block: IRectEntry.() -> Unit): IRectEntry { diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index f44d7e5df..46791bce6 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -9,6 +9,7 @@ import com.lambda.gui.api.component.core.list.IListComponent import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.gui.api.layer.RenderLayer import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings import com.lambda.util.KeyCode import com.lambda.util.Mouse import com.lambda.util.math.MathUtils.toInt @@ -47,7 +48,7 @@ abstract class WindowComponent : InteractiveComponent(), IL layer.rect.build { position = rect roundRadius = ClickGui.windowRadius - color(ClickGui.backgroundColor) + color(GuiSettings.backgroundColor) } // Title diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index c1d32c6ca..8812e4f23 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -5,6 +5,7 @@ import com.lambda.graphics.animation.AnimationTicker import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.ChildComponent import com.lambda.module.modules.client.ClickGui +import com.lambda.module.modules.client.GuiSettings import com.lambda.util.Mouse import com.lambda.util.math.ColorUtils.multAlpha import com.lambda.util.math.MathUtils.lerp @@ -40,7 +41,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C // Active color layer.rect.build { position = rect.shrink(interactAnimation) - color(ClickGui.mainColor.multAlpha(activeAnimation * 0.2)) + color(GuiSettings.mainColor.multAlpha(activeAnimation * 0.2)) } // Hover glint @@ -49,7 +50,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C position = hoverRect.shrink(interactAnimation) val alpha = interactAnimation * 0.3 - color(ClickGui.mainColor.multAlpha(alpha)) + color(GuiSettings.mainColor.multAlpha(alpha)) } // Toggle fx @@ -63,7 +64,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C // 0.0 .. 1.0 .. 0.0 animation val alpha = 1.0 - (abs(activeAnimation - 0.5) * 2.0) - val color = ClickGui.mainColor.multAlpha(alpha * 0.8) + val color = GuiSettings.mainColor.multAlpha(alpha * 0.8) // "Tail" effect val leftColor = color.multAlpha(1.0 - active.toInt()) @@ -77,7 +78,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C text = this@ButtonComponent.text scale = 1.0 - pressAnimation * 0.08 - color = lerp(Color.WHITE, ClickGui.mainColor, activeAnimation) + color = lerp(Color.WHITE, GuiSettings.mainColor, activeAnimation) val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation position = Vec2d(x, rect.center.y) diff --git a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt index d892ed121..fe871fc28 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/layer/RenderLayer.kt @@ -2,11 +2,7 @@ package com.lambda.gui.api.layer import com.lambda.graphics.renderer.gui.font.FontRenderer import com.lambda.graphics.renderer.gui.rect.RectRenderer -import com.lambda.module.modules.client.ClickGui -import com.mojang.blaze3d.systems.RenderSystem.blendFunc -import com.mojang.blaze3d.systems.RenderSystem.defaultBlendFunc -import org.lwjgl.opengl.GL11.GL_ONE -import org.lwjgl.opengl.GL11.GL_SRC_ALPHA +import com.lambda.module.modules.client.GuiSettings class RenderLayer(private val allowEffects: Boolean = false) { private val rectRenderer = RectRenderer() @@ -18,8 +14,11 @@ class RenderLayer(private val allowEffects: Boolean = false) { rect.update() font.update() - rectRenderer.shadeColor = ClickGui.shade - applyFancyBlending(rect::render) + rectRenderer.apply { + shadeColor = GuiSettings.shade && allowEffects + fancyBlending = GuiSettings.glow && allowEffects + render() + } font.render() } @@ -28,12 +27,4 @@ class RenderLayer(private val allowEffects: Boolean = false) { rect.destroy() font.destroy() } - - private fun applyFancyBlending(block: () -> Unit) { - if (ClickGui.glow && allowEffects) { - blendFunc(GL_SRC_ALPHA, GL_ONE) - block() - defaultBlendFunc() - } else block() - } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 2ca79db87..36ddf30da 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -13,33 +13,11 @@ object ClickGui : Module( description = "Sexy", defaultTags = setOf(ModuleTag.CLIENT) ) { - private val page by setting("Page", Page.Colors) - // General - val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) - val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1, visibility = { page == Page.General }) - val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1, visibility = { page == Page.General }) - val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1, visibility = { page == Page.General }) - - // Colors - private val primaryColor by setting("Primary Color", Color(130, 200, 255), visibility = { page == Page.Colors }) - private val secondaryColor by setting("Secondary Color", Color(225, 130, 225), visibility = { page == Page.Colors && shade }) - val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) - val glow by setting("Glow", true, visibility = { page == Page.Colors }) - val shade by setting("Shade Color", true, visibility = { page == Page.Colors }) - val colorWidth by setting("Color Width", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) - val colorHeight by setting("Color Height", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) - val colorSpeed by setting("Color Speed", 1.0, 0.1..10.0, 0.1, visibility = { page == Page.Colors && shade }) - - val mainColor: Color get() = if (shade) Color.WHITE else primaryColor - - val shadeColor1 get() = primaryColor - val shadeColor2 get() = secondaryColor - - enum class Page { - General, - Colors - } + val windowRadius by setting("Window Radius", 2.0, 0.0..10.0, 0.1) + val windowPadding by setting("Window Padding", 2.0, 0.0..10.0, 0.1) + val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) + val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1) private val gui by mainThread { LambdaClickGui() diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt new file mode 100644 index 000000000..1d4048dd0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt @@ -0,0 +1,37 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import java.awt.Color + +object GuiSettings : Module( + name = "HUD", + description = "Visual behaviour configuration", + defaultTags = setOf(ModuleTag.CLIENT) +) { + private val page by setting("Page", Page.General) + + private val scaleSetting by setting("Scale", 1.0, 0.5..3.0, 0.01, visibility = { page == Page.General }) + + private val primaryColor by setting("Primary Color", Color(130, 200, 255), visibility = { page == Page.Colors }) + private val secondaryColor by setting("Secondary Color", Color(225, 130, 225), visibility = { page == Page.Colors && shade }) + val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) + val glow by setting("Glow", true, visibility = { page == Page.Colors }) + val shade by setting("Shade Color", true, visibility = { page == Page.Colors }) + val colorWidth by setting("Color Width", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) + val colorHeight by setting("Color Height", 40.0, 1.0..100.0, 1.0, visibility = { page == Page.Colors && shade }) + val colorSpeed by setting("Color Speed", 1.0, 0.1..10.0, 0.1, visibility = { page == Page.Colors && shade }) + + val mainColor: Color get() = if (shade) Color.WHITE else primaryColor + + val shadeColor1 get() = primaryColor + val shadeColor2 get() = secondaryColor + + + enum class Page { + General, + Colors + } + + val scale get() = scaleSetting * 2 +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt b/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt deleted file mode 100644 index 975f57e0e..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/client/HUD.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.lambda.module.modules.client - -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag - -object HUD : Module( - name = "HUD", - description = "Visual behaviour configuration", - defaultTags = setOf(ModuleTag.CLIENT) -) { - val scale by setting("Scale", 2.0, 0.5..4.0, 0.01, description = "UI Scale factor") -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt index d3fb6d67c..f2f4bbcf6 100644 --- a/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/text/TextDsl.kt @@ -16,9 +16,6 @@ package com.lambda.util.text -import com.lambda.module.modules.client.HUD -import com.lambda.util.math.hsb -import com.lambda.util.math.readHSB import net.minecraft.text.* import net.minecraft.util.Identifier import java.awt.Color From 6dd4889e419db5bbd696076ace4c8f1bb459cf76 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 10 Apr 2024 15:15:20 +0300 Subject: [PATCH 34/46] hot --- .../main/kotlin/com/lambda/module/modules/client/GuiSettings.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt index 1d4048dd0..5be7acaa3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt @@ -27,7 +27,6 @@ object GuiSettings : Module( val shadeColor1 get() = primaryColor val shadeColor2 get() = secondaryColor - enum class Page { General, Colors From 391e9e5ec8696bd6885e5642107e8f80c09b7646 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Wed, 10 Apr 2024 15:16:50 +0300 Subject: [PATCH 35/46] 1 more mistake --- .../kotlin/com/lambda/module/modules/client/GuiSettings.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt index 5be7acaa3..60c98773f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/GuiSettings.kt @@ -5,14 +5,16 @@ import com.lambda.module.tag.ModuleTag import java.awt.Color object GuiSettings : Module( - name = "HUD", + name = "GuiSettings", description = "Visual behaviour configuration", defaultTags = setOf(ModuleTag.CLIENT) ) { private val page by setting("Page", Page.General) + // General private val scaleSetting by setting("Scale", 1.0, 0.5..3.0, 0.01, visibility = { page == Page.General }) + // Colors private val primaryColor by setting("Primary Color", Color(130, 200, 255), visibility = { page == Page.Colors }) private val secondaryColor by setting("Secondary Color", Color(225, 130, 225), visibility = { page == Page.Colors && shade }) val backgroundColor by setting("Background Color", Color(0, 0, 0, 80), visibility = { page == Page.Colors }) From 0b7246123dcd508ed8a473b4794f083e468012e5 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 14:48:17 +0200 Subject: [PATCH 36/46] Persistent GUI configuration --- common/src/main/kotlin/com/lambda/Lambda.kt | 3 ++ .../kotlin/com/lambda/config/Configurable.kt | 4 +- .../lambda/config/configurations/GuiConfig.kt | 9 ++++ .../serializer/gui/TagWindowSerializer.kt | 42 +++++++++++++++++++ .../settings/collections/ListSetting.kt | 6 +-- .../src/main/kotlin/com/lambda/core/Loader.kt | 4 ++ .../kotlin/com/lambda/gui/api/LambdaGui.kt | 13 +++--- .../gui/api/component/InteractiveComponent.kt | 8 +++- .../gui/api/component/WindowComponent.kt | 2 +- .../gui/impl/clickgui/LambdaClickGui.kt | 18 ++++---- .../gui/impl/clickgui/windows/TagWindow.kt | 18 +++++--- .../main/kotlin/com/lambda/module/Module.kt | 2 +- .../com/lambda/module/modules/Packetlogger.kt | 4 +- .../lambda/module/modules/client/ClickGui.kt | 12 ++---- .../kotlin/com/lambda/util/Communication.kt | 20 +++++---- 15 files changed, 119 insertions(+), 46 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt diff --git a/common/src/main/kotlin/com/lambda/Lambda.kt b/common/src/main/kotlin/com/lambda/Lambda.kt index f2acfcdd2..623bff357 100644 --- a/common/src/main/kotlin/com/lambda/Lambda.kt +++ b/common/src/main/kotlin/com/lambda/Lambda.kt @@ -3,7 +3,9 @@ package com.lambda import com.google.gson.Gson import com.google.gson.GsonBuilder import com.lambda.config.serializer.* +import com.lambda.config.serializer.gui.TagWindowSerializer import com.lambda.core.Loader +import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.tag.ModuleTag import com.lambda.util.KeyCode import net.minecraft.block.Block @@ -24,6 +26,7 @@ object Lambda { val gson: Gson = GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(ModuleTag::class.java, ModuleTagSerializer) + .registerTypeAdapter(TagWindow::class.java, TagWindowSerializer) .registerTypeAdapter(KeyCode::class.java, KeyCodeSerializer) .registerTypeAdapter(Color::class.java, ColorSerializer) .registerTypeAdapter(BlockPos::class.java, BlockPosSerializer) diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index be81458f8..fed5a4afc 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -149,7 +149,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { * The type parameter [T] must either be a primitive type or a type with a registered type adapter in [Lambda.gson]. * * @param name The unique identifier for the setting. - * @param defaultValue The default [List] value of type [T] for the setting. + * @param defaultValue The default [MutableList] value of type [T] for the setting. * @param description A brief explanation of the setting's purpose and behavior. * @param visibility A lambda expression that determines the visibility status of the setting. * @@ -162,7 +162,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { */ inline fun setting( name: String, - defaultValue: List, + defaultValue: MutableList, description: String = "", noinline visibility: () -> Boolean = { true }, ) = ListSetting(name, defaultValue, description, visibility).also { diff --git a/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt b/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt new file mode 100644 index 000000000..8b6a2fc59 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/configurations/GuiConfig.kt @@ -0,0 +1,9 @@ +package com.lambda.config.configurations + +import com.lambda.config.Configuration +import com.lambda.util.FolderRegister + +object GuiConfig : Configuration() { + override val configName = "gui" + override val primary = FolderRegister.config.resolve("$configName.json") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt new file mode 100644 index 000000000..e88c245ca --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -0,0 +1,42 @@ +package com.lambda.config.serializer.gui + +import com.google.gson.* +import com.lambda.gui.impl.clickgui.windows.TagWindow +import com.lambda.util.math.Vec2d +import java.lang.reflect.Type + +object TagWindowSerializer : JsonSerializer, JsonDeserializer { + + override fun serialize( + src: TagWindow?, + typeOfSrc: Type?, + context: JsonSerializationContext?, + ): JsonElement = src?.let { + JsonObject().apply { + addProperty("title", it.title) + addProperty("width", it.width) + addProperty("height", it.height) + addProperty("isOpen", it.isOpen) + add("position", JsonArray().apply { + add(it.position.x) + add(it.position.y) + }) + } + } ?: JsonNull.INSTANCE + + override fun deserialize( + json: JsonElement?, + typeOfT: Type?, + context: JsonDeserializationContext?, + ) = json?.asJsonObject?.let { + TagWindow().apply { + width = it["width"].asDouble + height = it["height"].asDouble + isOpen = it["isOpen"].asBoolean + position = Vec2d( + it["position"].asJsonArray[0].asDouble, + it["position"].asJsonArray[1].asDouble + ) + } + } ?: TagWindow() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index e2c687ffe..2f35f4f52 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -7,16 +7,16 @@ import com.lambda.config.AbstractSetting class ListSetting( override val name: String, - defaultValue: List, + defaultValue: MutableList, description: String, visibility: () -> Boolean, -) : AbstractSetting>( +) : AbstractSetting>( defaultValue, description, visibility ) { override fun loadFromJson(serialized: JsonElement) { - val listType = object : TypeToken>() {}.type + val listType = object : TypeToken>() {}.type value = gson.fromJson(serialized, listType) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index c72d46130..5e215f73f 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -3,7 +3,9 @@ package com.lambda.core import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.command.CommandManager +import com.lambda.config.configurations.GuiConfig import com.lambda.graphics.renderer.gui.font.LambdaFont +import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry @@ -35,5 +37,7 @@ object Loader { } LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") + + LambdaClickGui } } diff --git a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt index 82ca29565..1131da747 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/LambdaGui.kt @@ -1,7 +1,7 @@ package com.lambda.gui.api import com.lambda.Lambda.mc -import com.lambda.event.EventFlow +import com.lambda.event.EventFlow.syncListeners import com.lambda.event.events.RenderEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.UnsafeListener @@ -17,7 +17,10 @@ import net.minecraft.client.gui.screen.Screen import net.minecraft.text.Text @Suppress("LeakingThis") -abstract class LambdaGui(override val name: String, private val owner: Module? = null) : Screen(Text.of(name)), IComponent, Nameable { +abstract class LambdaGui( + override val name: String, + private val owner: Module? = null +) : Screen(Text.of(name)), IComponent, Nameable { private var screenSize = Vec2d.ZERO private val renderListener = UnsafeListener(0, this, false) { event -> @@ -38,7 +41,7 @@ abstract class LambdaGui(override val name: String, private val owner: Module? = fun show() { mc.currentScreen?.close() - recordRenderCall { // wait for previous screen to be closed + recordRenderCall { // wait for the previous screen to be closed mc.setScreen(this) } } @@ -46,7 +49,7 @@ abstract class LambdaGui(override val name: String, private val owner: Module? = final override fun onDisplayed() { onShow() - with(EventFlow.syncListeners) { + with(syncListeners) { subscribe(renderListener) subscribe(tickListener) } @@ -60,7 +63,7 @@ abstract class LambdaGui(override val name: String, private val owner: Module? = owner?.disable() mc.currentScreen = this - with(EventFlow.syncListeners) { + with(syncListeners) { unsubscribe(renderListener) unsubscribe(tickListener) } diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt index 5fbb85424..0bd6e7555 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/InteractiveComponent.kt @@ -29,8 +29,12 @@ abstract class InteractiveComponent : IComponent, IRectComponent { hovered = rect.contains(mouse) } - override fun onMouseClick(button: Mouse.Button, action: Mouse.Action, mouse: Vec2d) { - activeMouseButton = button.takeUnless { it.isMainButton && action == Mouse.Action.Click } + override fun onMouseClick( + button: Mouse.Button, action: Mouse.Action, mouse: Vec2d + ) { + activeMouseButton = button.takeUnless { + it.isMainButton && action == Mouse.Action.Click + } pressed = hovered && button.isMainButton && action == Mouse.Action.Click } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index 46791bce6..a1882db02 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -24,7 +24,7 @@ abstract class WindowComponent : InteractiveComponent(), IL var position = Vec2d.ZERO - private var isOpen = true + var isOpen = true private var dragOffset: Vec2d? = null private val padding get() = ClickGui.windowPadding diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index e94c1c013..1f4c79f9f 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -1,19 +1,23 @@ package com.lambda.gui.impl.clickgui +import com.lambda.command.CommandManager.setting +import com.lambda.config.Configurable +import com.lambda.config.configurations.GuiConfig import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.IListComponent -import com.lambda.gui.api.component.sub.ButtonComponent import com.lambda.gui.impl.clickgui.windows.TagWindow -import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui -import com.lambda.util.Mouse -import com.lambda.util.math.Vec2d -class LambdaClickGui : LambdaGui("Lambda ClickGui", ClickGui), IListComponent> { - override val children = mutableListOf>() +object LambdaClickGui : LambdaGui("ClickGui", ClickGui), IListComponent> { + private val windows = setting("windows", mutableListOf>(TagWindow())) + override val children: MutableList> get() = windows.value init { - children.add(TagWindow()) + object : Configurable(GuiConfig) { + init { settings.add(windows) } + + override val name = this@LambdaClickGui.name + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 4506443f3..87d129ddb 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -4,14 +4,18 @@ import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui +import com.lambda.module.tag.ModuleTag -class TagWindow : WindowComponent() { - override val title = "Test Window" - override var width = 110.0 - override var height = 300.0 - +class TagWindow( + private val tags: Set = emptySet(), + override val title: String = "Test Window", + override var width: Double = 110.0, + override var height: Double = 300.0 +) : WindowComponent() { init { - ModuleRegistry.modules.forEach { + ModuleRegistry.modules.filter { + it.customTags.value.any { tag -> tags.contains(tag) } || tags.isEmpty() + }.forEach { children.add(ModuleButton(it, this)) } } @@ -24,6 +28,8 @@ class TagWindow : WindowComponent() { private fun updateModules() { children.sortBy { it.module.name } + // ToDo: Update tag filter + children.forEachIndexed { i, button -> button.heightOffset = i * (ClickGui.buttonHeight + ClickGui.buttonStep) } diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index f5a1c607b..c64c4f422 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -98,7 +98,7 @@ abstract class Module( private val isEnabledSetting = setting("Enabled", enabledByDefault, visibility = { false }) private val keybindSetting = setting("Keybind", defaultKeybind) private val isVisible = setting("Visible", true) - private val customTags = setting("Tags", defaultTags, visibility = { false }) + val customTags = setting("Tags", defaultTags, visibility = { false }) var isEnabled by isEnabledSetting override val isMuted: Boolean diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index 5180f3180..d6f94aab5 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -37,8 +37,8 @@ object Packetlogger : Module( private val networkSide by setting("Network Side", NetworkSide.ANY, "Side of the network to log packets from") private val logTicks by setting("Log Ticks", true, "Show game ticks in the log") private val scope by setting("Scope", Scope.ANY, "Scope of packets to log") - private val whitelist by setting("Whitelist Packets", emptyList(), "Packets to whitelist") { scope == Scope.WHITELIST } - private val blacklist by setting("Blacklist Packets", emptyList(), "Packets to blacklist") { scope == Scope.BLACKLIST } + private val whitelist by setting("Whitelist Packets", mutableListOf(), "Packets to whitelist") { scope == Scope.WHITELIST } + private val blacklist by setting("Blacklist Packets", mutableListOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } private val maxRecursionDepth by setting("Max Recursion Depth", 6, 1..10, 1, "Maximum recursion depth for packet serialization") private val logConcurrent by setting("Build Data Concurrent", false, "Whether to serialize packets concurrently. Will not save packets in chronological order but wont lag the game.") diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt index 36ddf30da..4b18b7b80 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt @@ -19,20 +19,16 @@ object ClickGui : Module( val buttonHeight by setting("Button Height", 11.0, 8.0..20.0, 0.1) val buttonStep by setting("Button Step", 1.0, 0.0..5.0, 0.1) - private val gui by mainThread { - LambdaClickGui() - } - init { onEnable { - if (mc.currentScreen != gui) { - gui.show() + if (mc.currentScreen != LambdaClickGui) { + LambdaClickGui.show() } } onDisable { - if (mc.currentScreen == gui) { - gui.close() + if (mc.currentScreen == LambdaClickGui) { + LambdaClickGui.close() } } diff --git a/common/src/main/kotlin/com/lambda/util/Communication.kt b/common/src/main/kotlin/com/lambda/util/Communication.kt index 49c254617..e849d26ae 100644 --- a/common/src/main/kotlin/com/lambda/util/Communication.kt +++ b/common/src/main/kotlin/com/lambda/util/Communication.kt @@ -2,7 +2,9 @@ package com.lambda.util import com.lambda.Lambda import com.lambda.Lambda.mc +import com.lambda.threading.runOnGameThread import com.lambda.threading.runSafe +import com.lambda.threading.runSafeOnGameThread import com.lambda.util.StringUtils.capitalize import com.lambda.util.text.* import net.minecraft.client.toast.SystemToast @@ -34,10 +36,10 @@ object Communication { } fun Any.toast(message: Text, logLevel: LogLevel = LogLevel.INFO) { - runSafe { - buildText { - text(this@toast.source(logLevel, color = Color.YELLOW)) - }.let { title -> + buildText { + text(this@toast.source(logLevel, color = Color.YELLOW)) + }.let { title -> + runSafeOnGameThread { mc.toastManager.add(logLevel.toast(title, message)) } } @@ -53,11 +55,11 @@ object Communication { } fun Any.log(message: Text, logLevel: LogLevel = LogLevel.INFO, source: String = "", textSource: Text = Text.empty()) { - runSafe { - buildText { - text(this@log.source(logLevel, source, textSource)) - text(message) - }.let { log -> + buildText { + text(this@log.source(logLevel, source, textSource)) + text(message) + }.let { log -> + runSafeOnGameThread { player.sendMessage(log) } } From 47ade72670976e95925c59f56f3350e1e27be1d1 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 15:17:46 +0200 Subject: [PATCH 37/46] Fix double storing in lambda.json --- common/src/main/kotlin/com/lambda/Lambda.kt | 1 + .../serializer/{ => gui}/ModuleTagSerializer.kt | 2 +- .../lambda/gui/impl/clickgui/GuiConfigurable.kt | 11 +++++++++++ .../lambda/gui/impl/clickgui/LambdaClickGui.kt | 15 +-------------- 4 files changed, 14 insertions(+), 15 deletions(-) rename common/src/main/kotlin/com/lambda/config/serializer/{ => gui}/ModuleTagSerializer.kt (94%) create mode 100644 common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt diff --git a/common/src/main/kotlin/com/lambda/Lambda.kt b/common/src/main/kotlin/com/lambda/Lambda.kt index 623bff357..3a861bd35 100644 --- a/common/src/main/kotlin/com/lambda/Lambda.kt +++ b/common/src/main/kotlin/com/lambda/Lambda.kt @@ -3,6 +3,7 @@ package com.lambda import com.google.gson.Gson import com.google.gson.GsonBuilder import com.lambda.config.serializer.* +import com.lambda.config.serializer.gui.ModuleTagSerializer import com.lambda.config.serializer.gui.TagWindowSerializer import com.lambda.core.Loader import com.lambda.gui.impl.clickgui.windows.TagWindow diff --git a/common/src/main/kotlin/com/lambda/config/serializer/ModuleTagSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/ModuleTagSerializer.kt similarity index 94% rename from common/src/main/kotlin/com/lambda/config/serializer/ModuleTagSerializer.kt rename to common/src/main/kotlin/com/lambda/config/serializer/gui/ModuleTagSerializer.kt index c6c40cad5..c9665de04 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/ModuleTagSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/ModuleTagSerializer.kt @@ -1,4 +1,4 @@ -package com.lambda.config.serializer +package com.lambda.config.serializer.gui import com.google.gson.* import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt new file mode 100644 index 000000000..118cf126a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -0,0 +1,11 @@ +package com.lambda.gui.impl.clickgui + +import com.lambda.config.Configurable +import com.lambda.config.configurations.GuiConfig +import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.impl.clickgui.windows.TagWindow + +object GuiConfigurable : Configurable(GuiConfig) { + override val name = "ClickGui" + val windows = setting("windows", mutableListOf>(TagWindow())) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index 1f4c79f9f..dcc2baf3c 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -1,23 +1,10 @@ package com.lambda.gui.impl.clickgui -import com.lambda.command.CommandManager.setting -import com.lambda.config.Configurable -import com.lambda.config.configurations.GuiConfig import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.api.component.core.list.IListComponent -import com.lambda.gui.impl.clickgui.windows.TagWindow import com.lambda.module.modules.client.ClickGui object LambdaClickGui : LambdaGui("ClickGui", ClickGui), IListComponent> { - private val windows = setting("windows", mutableListOf>(TagWindow())) - override val children: MutableList> get() = windows.value - - init { - object : Configurable(GuiConfig) { - init { settings.add(windows) } - - override val name = this@LambdaClickGui.name - } - } + override val children: MutableList> get() = GuiConfigurable.windows.value } \ No newline at end of file From efb69ce96ee5ac52f307a4a69c8a53cfa8504048 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 15:29:27 +0200 Subject: [PATCH 38/46] Revert mutable list setting --- common/src/main/kotlin/com/lambda/config/Configurable.kt | 4 ++-- .../com/lambda/config/settings/collections/ListSetting.kt | 6 +++--- .../kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt | 2 +- .../main/kotlin/com/lambda/module/modules/Packetlogger.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index fed5a4afc..be81458f8 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -149,7 +149,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { * The type parameter [T] must either be a primitive type or a type with a registered type adapter in [Lambda.gson]. * * @param name The unique identifier for the setting. - * @param defaultValue The default [MutableList] value of type [T] for the setting. + * @param defaultValue The default [List] value of type [T] for the setting. * @param description A brief explanation of the setting's purpose and behavior. * @param visibility A lambda expression that determines the visibility status of the setting. * @@ -162,7 +162,7 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { */ inline fun setting( name: String, - defaultValue: MutableList, + defaultValue: List, description: String = "", noinline visibility: () -> Boolean = { true }, ) = ListSetting(name, defaultValue, description, visibility).also { diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index 2f35f4f52..e2c687ffe 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -7,16 +7,16 @@ import com.lambda.config.AbstractSetting class ListSetting( override val name: String, - defaultValue: MutableList, + defaultValue: List, description: String, visibility: () -> Boolean, -) : AbstractSetting>( +) : AbstractSetting>( defaultValue, description, visibility ) { override fun loadFromJson(serialized: JsonElement) { - val listType = object : TypeToken>() {}.type + val listType = object : TypeToken>() {}.type value = gson.fromJson(serialized, listType) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt index 118cf126a..f9bc5047c 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -7,5 +7,5 @@ import com.lambda.gui.impl.clickgui.windows.TagWindow object GuiConfigurable : Configurable(GuiConfig) { override val name = "ClickGui" - val windows = setting("windows", mutableListOf>(TagWindow())) + val windows = setting("windows", listOf>(TagWindow())) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index d6f94aab5..968229a6b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -37,8 +37,8 @@ object Packetlogger : Module( private val networkSide by setting("Network Side", NetworkSide.ANY, "Side of the network to log packets from") private val logTicks by setting("Log Ticks", true, "Show game ticks in the log") private val scope by setting("Scope", Scope.ANY, "Scope of packets to log") - private val whitelist by setting("Whitelist Packets", mutableListOf(), "Packets to whitelist") { scope == Scope.WHITELIST } - private val blacklist by setting("Blacklist Packets", mutableListOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } + private val whitelist by setting("Whitelist Packets", listOf(), "Packets to whitelist") { scope == Scope.WHITELIST } + private val blacklist by setting("Blacklist Packets", listOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } private val maxRecursionDepth by setting("Max Recursion Depth", 6, 1..10, 1, "Maximum recursion depth for packet serialization") private val logConcurrent by setting("Build Data Concurrent", false, "Whether to serialize packets concurrently. Will not save packets in chronological order but wont lag the game.") From 1bb782eb490f7fbaac54a39e6b3269dde5c9a29a Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 15:30:51 +0200 Subject: [PATCH 39/46] Forgor --- .../main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt index dcc2baf3c..b24ccf7a0 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/LambdaClickGui.kt @@ -6,5 +6,5 @@ import com.lambda.gui.api.component.core.list.IListComponent import com.lambda.module.modules.client.ClickGui object LambdaClickGui : LambdaGui("ClickGui", ClickGui), IListComponent> { - override val children: MutableList> get() = GuiConfigurable.windows.value + override val children: List> get() = GuiConfigurable.windows.value } \ No newline at end of file From b96c0b96a3ee2409189cc696f22c0372b9190046 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 15:33:57 +0200 Subject: [PATCH 40/46] Test object loading --- common/src/main/kotlin/com/lambda/core/Loader.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 5e215f73f..db2b6c0c1 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -5,6 +5,7 @@ import com.lambda.Lambda.LOG import com.lambda.command.CommandManager import com.lambda.config.configurations.GuiConfig import com.lambda.graphics.renderer.gui.font.LambdaFont +import com.lambda.gui.impl.clickgui.GuiConfigurable import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager @@ -38,6 +39,7 @@ object Loader { LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") + GuiConfigurable LambdaClickGui } } From ee4e415e74a59d6d6588bc3c92753f7bf43cb8c9 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 10 Apr 2024 16:02:24 +0200 Subject: [PATCH 41/46] Tighter type constraint --- .../com/lambda/config/settings/collections/ListSetting.kt | 7 ++++++- .../kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index e2c687ffe..320b614e4 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -2,8 +2,10 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken +import com.lambda.Lambda.LOG import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting +import com.lambda.util.DynamicReflectionSerializer.dynamicString class ListSetting( override val name: String, @@ -17,6 +19,9 @@ class ListSetting( ) { override fun loadFromJson(serialized: JsonElement) { val listType = object : TypeToken>() {}.type - value = gson.fromJson(serialized, listType) + LOG.info("Loading $name with value $serialized current value ${value.dynamicString()} $value and type ${listType.typeName}") + val dese = gson.fromJson>(serialized, listType) + value = dese + LOG.info("Loaded $name with value ${value.dynamicString()} $value and type ${listType.typeName}") } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt index f9bc5047c..5067c56fc 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -2,10 +2,9 @@ package com.lambda.gui.impl.clickgui import com.lambda.config.Configurable import com.lambda.config.configurations.GuiConfig -import com.lambda.gui.api.component.WindowComponent import com.lambda.gui.impl.clickgui.windows.TagWindow object GuiConfigurable : Configurable(GuiConfig) { override val name = "ClickGui" - val windows = setting("windows", listOf>(TagWindow())) + val windows = setting("windows", listOf(TagWindow())) } \ No newline at end of file From db2070f91fdfbdd73fc31a09ae2eb9adc0170852 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 11 Apr 2024 05:23:32 +0200 Subject: [PATCH 42/46] Fix type erasure for all collection settings --- .../kotlin/com/lambda/config/Configurable.kt | 10 +++++++--- .../kotlin/com/lambda/config/Configuration.kt | 2 +- .../serializer/gui/TagWindowSerializer.kt | 19 +++++++++++++++++-- .../settings/collections/ListSetting.kt | 13 ++++--------- .../config/settings/collections/MapSetting.kt | 6 +++--- .../config/settings/collections/SetSetting.kt | 6 +++--- .../gui/impl/clickgui/GuiConfigurable.kt | 5 +++-- .../gui/impl/clickgui/windows/TagWindow.kt | 4 ++-- .../main/kotlin/com/lambda/module/Module.kt | 3 ++- .../com/lambda/module/modules/Packetlogger.kt | 5 +++-- 10 files changed, 45 insertions(+), 28 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index be81458f8..ae88e0a81 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -21,6 +21,7 @@ import com.lambda.util.Nameable import net.minecraft.block.Block import net.minecraft.util.math.BlockPos import java.awt.Color +import java.lang.reflect.Type /** * Represents a set of [AbstractSetting]s that are associated with the [name] of the [Configurable]. @@ -163,9 +164,10 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: List, + type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = ListSetting(name, defaultValue, description, visibility).also { + ) = ListSetting(name, defaultValue, type, description, visibility).also { settings.add(it) } @@ -189,9 +191,10 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: Map, + type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = MapSetting(name, defaultValue, description, visibility).also { + ) = MapSetting(name, defaultValue, type, description, visibility).also { settings.add(it) } @@ -215,9 +218,10 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: Set, + type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = SetSetting(name, defaultValue, description, visibility).also { + ) = SetSetting(name, defaultValue, type, description, visibility).also { settings.add(it) } diff --git a/common/src/main/kotlin/com/lambda/config/Configuration.kt b/common/src/main/kotlin/com/lambda/config/Configuration.kt index e2bdd989a..3dc6768a7 100644 --- a/common/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/common/src/main/kotlin/com/lambda/config/Configuration.kt @@ -90,7 +90,7 @@ abstract class Configuration : Jsonable { } .onFailure { val message = "Failed to load ${configName.capitalize()} config, loading backup" - LOG.error(message) + LOG.error(message, it) this@Configuration.logError(message) runCatching { load(backup) } .onSuccess { diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt index e88c245ca..d1b00df51 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -2,6 +2,7 @@ package com.lambda.config.serializer.gui import com.google.gson.* import com.lambda.gui.impl.clickgui.windows.TagWindow +import com.lambda.module.tag.ModuleTag import com.lambda.util.math.Vec2d import java.lang.reflect.Type @@ -14,6 +15,15 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer + add(tag.name) +// add(JsonObject().apply { +// addProperty("name", tag.name) +// addProperty("color", tag.color.rgb) +// }) + } + }) addProperty("width", it.width) addProperty("height", it.height) addProperty("isOpen", it.isOpen) @@ -29,9 +39,14 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer + ModuleTag(tag.asString) + }.toSet(), + title = it["title"].asString, + width = it["width"].asDouble, height = it["height"].asDouble + ).apply { isOpen = it["isOpen"].asBoolean position = Vec2d( it["position"].asJsonArray[0].asDouble, diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt index 320b614e4..cb6452996 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/ListSetting.kt @@ -1,15 +1,14 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement -import com.google.gson.reflect.TypeToken -import com.lambda.Lambda.LOG import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting -import com.lambda.util.DynamicReflectionSerializer.dynamicString +import java.lang.reflect.Type -class ListSetting( +class ListSetting( override val name: String, defaultValue: List, + private val type: Type, description: String, visibility: () -> Boolean, ) : AbstractSetting>( @@ -18,10 +17,6 @@ class ListSetting( visibility ) { override fun loadFromJson(serialized: JsonElement) { - val listType = object : TypeToken>() {}.type - LOG.info("Loading $name with value $serialized current value ${value.dynamicString()} $value and type ${listType.typeName}") - val dese = gson.fromJson>(serialized, listType) - value = dese - LOG.info("Loaded $name with value ${value.dynamicString()} $value and type ${listType.typeName}") + value = gson.fromJson(serialized, type) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt index 0c1b5e46f..a9190f4d5 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/MapSetting.kt @@ -1,13 +1,14 @@ package com.lambda.config.settings.collections -import com.google.common.reflect.TypeToken import com.google.gson.JsonElement import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting +import java.lang.reflect.Type class MapSetting( override val name: String, defaultValue: Map, + private val type: Type, description: String, visibility: () -> Boolean, ) : AbstractSetting>( @@ -16,7 +17,6 @@ class MapSetting( visibility ) { override fun loadFromJson(serialized: JsonElement) { - val mapType = object : TypeToken>() {}.type - value = gson.fromJson(serialized, mapType) + value = gson.fromJson(serialized, type) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt index b0379cce4..5bc7b8264 100644 --- a/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt +++ b/common/src/main/kotlin/com/lambda/config/settings/collections/SetSetting.kt @@ -1,13 +1,14 @@ package com.lambda.config.settings.collections import com.google.gson.JsonElement -import com.google.gson.reflect.TypeToken import com.lambda.Lambda.gson import com.lambda.config.AbstractSetting +import java.lang.reflect.Type class SetSetting( override val name: String, defaultValue: Set, + private val type: Type, description: String, visibility: () -> Boolean, ) : AbstractSetting>( @@ -16,7 +17,6 @@ class SetSetting( visibility ) { override fun loadFromJson(serialized: JsonElement) { - val setType = object : TypeToken>() {}.type - value = gson.fromJson(serialized, setType) + value = gson.fromJson(serialized, type) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt index 5067c56fc..6d3aefc99 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -1,10 +1,11 @@ package com.lambda.gui.impl.clickgui +import com.google.gson.reflect.TypeToken import com.lambda.config.Configurable import com.lambda.config.configurations.GuiConfig import com.lambda.gui.impl.clickgui.windows.TagWindow object GuiConfigurable : Configurable(GuiConfig) { - override val name = "ClickGui" - val windows = setting("windows", listOf(TagWindow())) + override val name = "gui" + val windows = setting("windows", listOf(TagWindow()), object : TypeToken>() {}.type) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 87d129ddb..af47ccf7b 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -7,8 +7,8 @@ import com.lambda.module.modules.client.ClickGui import com.lambda.module.tag.ModuleTag class TagWindow( - private val tags: Set = emptySet(), - override val title: String = "Test Window", + val tags: Set = emptySet(), + override var title: String = "Test Window", override var width: Double = 110.0, override var height: Double = 300.0 ) : WindowComponent() { diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index c64c4f422..4853b0b40 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -1,5 +1,6 @@ package com.lambda.module +import com.google.gson.reflect.TypeToken import com.lambda.config.AbstractSetting import com.lambda.config.Configurable import com.lambda.config.Configuration @@ -98,7 +99,7 @@ abstract class Module( private val isEnabledSetting = setting("Enabled", enabledByDefault, visibility = { false }) private val keybindSetting = setting("Keybind", defaultKeybind) private val isVisible = setting("Visible", true) - val customTags = setting("Tags", defaultTags, visibility = { false }) + val customTags = setting("Tags", defaultTags, object : TypeToken>() {}.type, visibility = { false }) var isEnabled by isEnabledSetting override val isMuted: Boolean diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index 968229a6b..bbf5e82ba 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -1,5 +1,6 @@ package com.lambda.module.modules +import com.google.gson.reflect.TypeToken import com.lambda.Lambda import com.lambda.Lambda.mc import com.lambda.event.EventFlow.lambdaScope @@ -37,8 +38,8 @@ object Packetlogger : Module( private val networkSide by setting("Network Side", NetworkSide.ANY, "Side of the network to log packets from") private val logTicks by setting("Log Ticks", true, "Show game ticks in the log") private val scope by setting("Scope", Scope.ANY, "Scope of packets to log") - private val whitelist by setting("Whitelist Packets", listOf(), "Packets to whitelist") { scope == Scope.WHITELIST } - private val blacklist by setting("Blacklist Packets", listOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } + private val whitelist by setting("Whitelist Packets", listOf(), object : TypeToken>() {}.type, "Packets to whitelist") { scope == Scope.WHITELIST } + private val blacklist by setting("Blacklist Packets", listOf(), object : TypeToken>() {}.type, "Packets to blacklist") { scope == Scope.BLACKLIST } private val maxRecursionDepth by setting("Max Recursion Depth", 6, 1..10, 1, "Maximum recursion depth for packet serialization") private val logConcurrent by setting("Build Data Concurrent", false, "Whether to serialize packets concurrently. Will not save packets in chronological order but wont lag the game.") From 5f5c2db6e2043c565e0a7fdf2e3904b664189ac8 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 11 Apr 2024 05:47:08 +0200 Subject: [PATCH 43/46] Initialization obscurity --- common/src/main/kotlin/com/lambda/core/Loader.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index db2b6c0c1..ff7ab72ef 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -39,7 +39,6 @@ object Loader { LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") - GuiConfigurable - LambdaClickGui + GuiConfigurable // ToDo: Find more elegant solution } } From 7b975301666c01b3803bd279de178c9b18db19ce Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 11 Apr 2024 15:07:20 +0300 Subject: [PATCH 44/46] Setting Refactor --- .../kotlin/com/lambda/config/Configurable.kt | 10 ++++------ .../config/serializer/gui/TagWindowSerializer.kt | 16 +++------------- .../lambda/gui/impl/clickgui/GuiConfigurable.kt | 4 ++-- .../gui/impl/clickgui/windows/TagWindow.kt | 10 +++++----- .../src/main/kotlin/com/lambda/module/Module.kt | 2 +- .../com/lambda/module/modules/Packetlogger.kt | 4 ++-- 6 files changed, 17 insertions(+), 29 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/Configurable.kt b/common/src/main/kotlin/com/lambda/config/Configurable.kt index ae88e0a81..00e091f41 100644 --- a/common/src/main/kotlin/com/lambda/config/Configurable.kt +++ b/common/src/main/kotlin/com/lambda/config/Configurable.kt @@ -2,6 +2,7 @@ package com.lambda.config import com.google.gson.JsonElement import com.google.gson.JsonObject +import com.google.gson.reflect.TypeToken import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.config.settings.CharSetting @@ -164,10 +165,9 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: List, - type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = ListSetting(name, defaultValue, type, description, visibility).also { + ) = ListSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { settings.add(it) } @@ -191,10 +191,9 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: Map, - type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = MapSetting(name, defaultValue, type, description, visibility).also { + ) = MapSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { settings.add(it) } @@ -218,10 +217,9 @@ abstract class Configurable(configuration: Configuration) : Jsonable, Nameable { inline fun setting( name: String, defaultValue: Set, - type: Type, description: String = "", noinline visibility: () -> Boolean = { true }, - ) = SetSetting(name, defaultValue, type, description, visibility).also { + ) = SetSetting(name, defaultValue, object : TypeToken>() {}.type, description, visibility).also { settings.add(it) } diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt index d1b00df51..b77ff4985 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -15,15 +15,7 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer - add(tag.name) -// add(JsonObject().apply { -// addProperty("name", tag.name) -// addProperty("color", tag.color.rgb) -// }) - } - }) + addProperty("tag", it.tag.name) addProperty("width", it.width) addProperty("height", it.height) addProperty("isOpen", it.isOpen) @@ -40,9 +32,7 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer - ModuleTag(tag.asString) - }.toSet(), + tag = ModuleTag(it["tag"].asString), title = it["title"].asString, width = it["width"].asDouble, height = it["height"].asDouble @@ -53,5 +43,5 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer>() {}.type) + val windows = setting("windows", listOf(TagWindow(ModuleTag("Test Window")))) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index af47ccf7b..852e73e19 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -7,15 +7,15 @@ import com.lambda.module.modules.client.ClickGui import com.lambda.module.tag.ModuleTag class TagWindow( - val tags: Set = emptySet(), - override var title: String = "Test Window", + val tag: ModuleTag, + override var title: String = tag.name, override var width: Double = 110.0, override var height: Double = 300.0 ) : WindowComponent() { init { - ModuleRegistry.modules.filter { - it.customTags.value.any { tag -> tags.contains(tag) } || tags.isEmpty() - }.forEach { + ModuleRegistry.modules/*.filter { module -> + module.customTags.value.any { it.name.equals(tag.name, true) } + }*/.forEach { children.add(ModuleButton(it, this)) } } diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 4853b0b40..545bc6195 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -99,7 +99,7 @@ abstract class Module( private val isEnabledSetting = setting("Enabled", enabledByDefault, visibility = { false }) private val keybindSetting = setting("Keybind", defaultKeybind) private val isVisible = setting("Visible", true) - val customTags = setting("Tags", defaultTags, object : TypeToken>() {}.type, visibility = { false }) + val customTags = setting("Tags", defaultTags, visibility = { false }) var isEnabled by isEnabledSetting override val isMuted: Boolean diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index bbf5e82ba..f2a44e6e4 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -38,8 +38,8 @@ object Packetlogger : Module( private val networkSide by setting("Network Side", NetworkSide.ANY, "Side of the network to log packets from") private val logTicks by setting("Log Ticks", true, "Show game ticks in the log") private val scope by setting("Scope", Scope.ANY, "Scope of packets to log") - private val whitelist by setting("Whitelist Packets", listOf(), object : TypeToken>() {}.type, "Packets to whitelist") { scope == Scope.WHITELIST } - private val blacklist by setting("Blacklist Packets", listOf(), object : TypeToken>() {}.type, "Packets to blacklist") { scope == Scope.BLACKLIST } + private val whitelist by setting("Whitelist Packets", listOf(), "Packets to whitelist") { scope == Scope.WHITELIST } + private val blacklist by setting("Blacklist Packets", listOf(), "Packets to blacklist") { scope == Scope.BLACKLIST } private val maxRecursionDepth by setting("Max Recursion Depth", 6, 1..10, 1, "Maximum recursion depth for packet serialization") private val logConcurrent by setting("Build Data Concurrent", false, "Whether to serialize packets concurrently. Will not save packets in chronological order but wont lag the game.") From 06acbfee693b9dccbd2243c06ec190e96cc0d45a Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 11 Apr 2024 17:22:34 +0300 Subject: [PATCH 45/46] revert: Multiple tag support --- .../config/serializer/gui/TagWindowSerializer.kt | 11 ++++++++--- .../com/lambda/gui/impl/clickgui/windows/TagWindow.kt | 10 +++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt index b77ff4985..337e34474 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/gui/TagWindowSerializer.kt @@ -7,7 +7,6 @@ import com.lambda.util.math.Vec2d import java.lang.reflect.Type object TagWindowSerializer : JsonSerializer, JsonDeserializer { - override fun serialize( src: TagWindow?, typeOfSrc: Type?, @@ -15,7 +14,11 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer + add(tag.name) + } + }) addProperty("width", it.width) addProperty("height", it.height) addProperty("isOpen", it.isOpen) @@ -32,7 +35,9 @@ object TagWindowSerializer : JsonSerializer, JsonDeserializer + ModuleTag(tag.asString) + }.toSet(), title = it["title"].asString, width = it["width"].asDouble, height = it["height"].asDouble diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 852e73e19..4e47077b9 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -7,15 +7,15 @@ import com.lambda.module.modules.client.ClickGui import com.lambda.module.tag.ModuleTag class TagWindow( - val tag: ModuleTag, - override var title: String = tag.name, + val tags: Set = setOf(), + override var title: String = "Untitled", override var width: Double = 110.0, override var height: Double = 300.0 ) : WindowComponent() { init { - ModuleRegistry.modules/*.filter { module -> - module.customTags.value.any { it.name.equals(tag.name, true) } - }*/.forEach { + ModuleRegistry.modules.filter { module -> + module.customTags.value.any(tags::contains) + }.forEach { children.add(ModuleButton(it, this)) } } From 3559754a32f42ce1fbe9f03ece1e6d0b74a39b60 Mon Sep 17 00:00:00 2001 From: Blade-gl Date: Thu, 11 Apr 2024 19:32:13 +0300 Subject: [PATCH 46/46] Better animations, refactor --- .../gui/api/component/WindowComponent.kt | 26 ++++++++++++++----- .../api/component/core/list/ChildComponent.kt | 2 +- .../api/component/core/list/IListComponent.kt | 2 +- .../gui/api/component/sub/ButtonComponent.kt | 23 ++++++++++++---- .../gui/impl/clickgui/GuiConfigurable.kt | 3 +-- .../gui/impl/clickgui/windows/TagWindow.kt | 10 ++++--- 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt index a1882db02..f6e13ced9 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/WindowComponent.kt @@ -15,6 +15,7 @@ import com.lambda.util.Mouse import com.lambda.util.math.MathUtils.toInt import com.lambda.util.math.Rect import com.lambda.util.math.Vec2d +import kotlin.math.abs abstract class WindowComponent : InteractiveComponent(), IListComponent { abstract val title: String @@ -41,7 +42,8 @@ abstract class WindowComponent : InteractiveComponent(), IL override val children = mutableListOf() val subLayer = RenderLayer(true) - private val renderHeight by animation.exp({ 0.0 }, { height + padding * 2 * isOpen.toInt() }, 0.5, ::isOpen) + private val actualHeight get() = height + padding * 2 * isOpen.toInt() + private var renderHeight by animation.exp({ 0.0 }, ::actualHeight, 0.5, ::isOpen) init { // Background @@ -63,6 +65,7 @@ abstract class WindowComponent : InteractiveComponent(), IL super.onShow() dragOffset = null + renderHeight = 0.0 } override fun onHide() { @@ -72,12 +75,12 @@ abstract class WindowComponent : InteractiveComponent(), IL override fun onTick() { animation.tick() - children.forEach { child -> - child.visible = isChildAccessible(child) + setChildrenAccessibility { child -> + child.rect in contentRect } children - .filter(ChildComponent::visible) + .filter(ChildComponent::accessible) .forEach(IComponent::onTick) } @@ -115,13 +118,22 @@ abstract class WindowComponent : InteractiveComponent(), IL if (mouse in titleBar && action == Mouse.Action.Click) { when(button) { Mouse.Button.Left -> dragOffset = mouse - position - Mouse.Button.Right -> isOpen = !isOpen + Mouse.Button.Right -> { + // Don't let user spam + val targetHeight = if (isOpen) actualHeight else 0.0 + if (abs(targetHeight - renderHeight) > 1) return + + isOpen = !isOpen + } } } super.onMouseClick(button, action, mouse) } - override fun isChildAccessible(child: T) = - child.rect in contentRect + private fun setChildrenAccessibility(flag: (T) -> Boolean) { + children.forEach { child -> + child.accessible = flag(child) + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt index 494c4ae93..c8209122f 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/ChildComponent.kt @@ -4,7 +4,7 @@ import com.lambda.gui.api.component.InteractiveComponent abstract class ChildComponent : InteractiveComponent(), IChildComponent { // mostly used to create an animation when an element appears - var visible = false; set(value) { + var accessible = false; set(value) { if (field == value) return field = value diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt index dd8b2f60d..303ea9ff5 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/core/list/IListComponent.kt @@ -8,7 +8,7 @@ import com.lambda.util.math.Vec2d interface IListComponent : IComponent { val children: List - fun isChildAccessible(child: T): Boolean = true + fun isChildAccessible(child: T): Boolean = (child as? ChildComponent)?.accessible ?: true override fun onShow() { children.forEach(IComponent::onShow) diff --git a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt index 8812e4f23..4e1265ead 100644 --- a/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt +++ b/common/src/main/kotlin/com/lambda/gui/api/component/sub/ButtonComponent.kt @@ -29,9 +29,10 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C private val animation = AnimationTicker() private var activeAnimation by animation.exp(0.0, 1.0, 0.1, ::active) - private var hoverAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered || active) 0.5 else 0.1 }, ::renderHovered) + private var hoverRectAnimation by animation.exp({ 0.0 }, { 1.0 }, { if (renderHovered) 0.5 else 0.1 }, ::renderHovered) + private var hoverFontAnimation by animation.exp(0.0, 1.0, 0.5, ::renderHovered) private var pressAnimation by animation.exp(0.0, 1.0, 0.5, ::pressed) - private val interactAnimation get() = lerp(hoverAnimation, 1.5, pressAnimation) * 0.4 + private val interactAnimation get() = lerp(hoverRectAnimation, 1.5, pressAnimation) * 0.4 private var lastHoveredTime = 0L private val renderHovered get() = hovered || @@ -46,7 +47,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C // Hover glint layer.rect.build { - val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverAnimation, rect.size.y) + val hoverRect = Rect.basedOn(rect.leftTop, rect.size.x * hoverRectAnimation, rect.size.y) position = hoverRect.shrink(interactAnimation) val alpha = interactAnimation * 0.3 @@ -80,7 +81,7 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C color = lerp(Color.WHITE, GuiSettings.mainColor, activeAnimation) - val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverAnimation + val x = rect.left + ClickGui.windowPadding + interactAnimation + hoverFontAnimation * 2.0 position = Vec2d(x, rect.center.y) } } @@ -89,7 +90,12 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C override fun onShow() { super.onShow() - activeAnimation = 0.0 + reset() + } + + override fun onHide() { + super.onHide() + reset() } override fun onTick() { @@ -107,6 +113,13 @@ abstract class ButtonComponent(final override val owner: WindowComponent<*>) : C if (hovered) lastHoveredTime = time } + private fun reset() { + activeAnimation = 0.0 + hoverRectAnimation = 0.0 + pressAnimation = 0.0 + lastHoveredTime = 0L + } + companion object { const val FILL_PARENT = -1.0 } diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt index 4111b2f3a..79158c61d 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/GuiConfigurable.kt @@ -3,9 +3,8 @@ package com.lambda.gui.impl.clickgui import com.lambda.config.Configurable import com.lambda.config.configurations.GuiConfig import com.lambda.gui.impl.clickgui.windows.TagWindow -import com.lambda.module.tag.ModuleTag object GuiConfigurable : Configurable(GuiConfig) { override val name = "gui" - val windows = setting("windows", listOf(TagWindow(ModuleTag("Test Window")))) + val windows = setting("windows", listOf(TagWindow())) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt index 4e47077b9..7249a0e11 100644 --- a/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt +++ b/common/src/main/kotlin/com/lambda/gui/impl/clickgui/windows/TagWindow.kt @@ -1,6 +1,9 @@ package com.lambda.gui.impl.clickgui.windows +import com.lambda.gui.api.LambdaGui import com.lambda.gui.api.component.WindowComponent +import com.lambda.gui.api.component.core.list.IChildComponent +import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.gui.impl.clickgui.buttons.ModuleButton import com.lambda.module.ModuleRegistry import com.lambda.module.modules.client.ClickGui @@ -10,11 +13,12 @@ class TagWindow( val tags: Set = setOf(), override var title: String = "Untitled", override var width: Double = 110.0, - override var height: Double = 300.0 -) : WindowComponent() { + override var height: Double = 300.0, + override val owner: LambdaGui = LambdaClickGui +) : WindowComponent(), IChildComponent { init { ModuleRegistry.modules.filter { module -> - module.customTags.value.any(tags::contains) + module.customTags.value.any(tags::contains) || tags.isEmpty() }.forEach { children.add(ModuleButton(it, this)) }