aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils/HolographicEntities.kt
blob: c4de45cbc00805f80e7dfa9e6dcf9eff7e484ad3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package at.hannibal2.skyhanni.utils

import at.hannibal2.skyhanni.mixins.transformers.AccessorRendererLivingEntity
import at.hannibal2.skyhanni.utils.RenderUtils.getViewerPos
import at.hannibal2.skyhanni.utils.TimeUtils.inWholeTicks
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.client.renderer.entity.RendererLivingEntity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.boss.EntityWither
import net.minecraft.entity.item.EntityArmorStand
import net.minecraft.entity.monster.EntityBlaze
import net.minecraft.entity.monster.EntityCaveSpider
import net.minecraft.entity.monster.EntityCreeper
import net.minecraft.entity.monster.EntityEnderman
import net.minecraft.entity.monster.EntityEndermite
import net.minecraft.entity.monster.EntityGhast
import net.minecraft.entity.monster.EntityGiantZombie
import net.minecraft.entity.monster.EntityGuardian
import net.minecraft.entity.monster.EntityIronGolem
import net.minecraft.entity.monster.EntityMagmaCube
import net.minecraft.entity.monster.EntityPigZombie
import net.minecraft.entity.monster.EntitySilverfish
import net.minecraft.entity.monster.EntitySkeleton
import net.minecraft.entity.monster.EntitySlime
import net.minecraft.entity.monster.EntitySnowman
import net.minecraft.entity.monster.EntitySpider
import net.minecraft.entity.monster.EntityWitch
import net.minecraft.entity.monster.EntityZombie
import net.minecraft.entity.passive.EntityBat
import net.minecraft.entity.passive.EntityChicken
import net.minecraft.entity.passive.EntityCow
import net.minecraft.entity.passive.EntityHorse
import net.minecraft.entity.passive.EntityMooshroom
import net.minecraft.entity.passive.EntityOcelot
import net.minecraft.entity.passive.EntityPig
import net.minecraft.entity.passive.EntityRabbit
import net.minecraft.entity.passive.EntitySheep
import net.minecraft.entity.passive.EntitySquid
import net.minecraft.entity.passive.EntityVillager
import net.minecraft.entity.passive.EntityWolf
import org.lwjgl.opengl.GL11

/**
 * Utility for creating fake entities without an associated world in order to avoid contaminating the world state.
 */
object HolographicEntities {

    /**
     * An instance of a holographic entity. Maintains a minimal controlled state,
     * which has just enough information for rendering and basic manipulations, such as
     * interpolated positioning. The underlying [entity] should not be accessed directly.
     */
    class HolographicEntity<T : EntityLivingBase> internal constructor(
        val entity: T,
        var position: LorenzVec,
        var yaw: Float,
    ) {
        var isChild: Boolean = false
        var lastPosition: LorenzVec = position
        var lastYaw: Float = yaw
        val createdAt = SimpleTimeMark.now()

        val monotonicProgress get() = createdAt.passedSince().inWholeTicks

        /**
         * Should be called exactly once per tick or never over the lifetime of this [HolographicEntity].
         */
        fun moveTo(position: LorenzVec, yaw: Float, isTeleport: Boolean = false) {
            if (isTeleport) {
                this.lastYaw = yaw
                this.lastPosition = position
            } else {
                this.lastYaw = this.yaw
                this.lastPosition = this.position
            }
            this.position = position
            this.yaw = yaw
        }

        fun interpolatedPosition(partialTicks: Float): LorenzVec {
            return lastPosition.slope(position, partialTicks.toDouble())
        }

        fun interpolatedYaw(partialTicks: Float): Float {
            return interpolateRotation(lastYaw, yaw, partialTicks)
        }
    }

    /**
     * Template for a [HolographicEntity]. This class exists as a guard for
     * [HolographicEntity] to prevent untested entities with potential NPEs
     * being instantiated. A list of tested entities exist in [HolographicEntities].
     * Some of these entities rely on mixins from NEU for their proper null
     * world handling.
     */
    class HolographicBase<T : EntityLivingBase> internal constructor(
        private val entity: T
    ) {
        fun instance(position: LorenzVec, yaw: Float): HolographicEntity<T> {
            return HolographicEntity(entity, position, yaw)
        }
    }

    val zombie = HolographicBase(EntityZombie(null))
    val chicken = HolographicBase(EntityChicken(null))
    val slime = HolographicBase(EntitySlime(null))
    val wolf = HolographicBase(EntityWolf(null))
    val skeleton = HolographicBase(EntitySkeleton(null))
    val creeper = HolographicBase(EntityCreeper(null))
    val ocelot = HolographicBase(EntityOcelot(null))
    val blaze = HolographicBase(EntityBlaze(null))
    val rabbit = HolographicBase(EntityRabbit(null))
    val sheep = HolographicBase(EntitySheep(null))
    val horse = HolographicBase(EntityHorse(null))
    val eisengolem = HolographicBase(EntityIronGolem(null))
    val silverfish = HolographicBase(EntitySilverfish(null))
    val witch = HolographicBase(EntityWitch(null))
    val endermite = HolographicBase(EntityEndermite(null))
    val snowman = HolographicBase(EntitySnowman(null))
    val villager = HolographicBase(EntityVillager(null))
    val guardian = HolographicBase(EntityGuardian(null))
    val armorStand = HolographicBase(EntityArmorStand(null))
    val squid = HolographicBase(EntitySquid(null))
    val bat = HolographicBase(EntityBat(null))
    val spider = HolographicBase(EntitySpider(null))
    val caveSpider = HolographicBase(EntityCaveSpider(null))
    val pigman = HolographicBase(EntityPigZombie(null))
    val ghast = HolographicBase(EntityGhast(null))
    val magmaCube = HolographicBase(EntityMagmaCube(null))
    val wither = HolographicBase(EntityWither(null))
    val enderman = HolographicBase(EntityEnderman(null))
    val mooshroom = HolographicBase(EntityMooshroom(null))
    val witherSkeleton = HolographicBase(EntitySkeleton(null).also { it.skeletonType = 1 })
    val cow = HolographicBase(EntityCow(null))
    val pig = HolographicBase(EntityPig(null))
    val giant = HolographicBase(EntityGiantZombie(null))

    private fun interpolateRotation(last: Float, next: Float, progress: Float): Float {
        var direction: Float = next - last
        while (direction < -180.0f) {
            direction += 360.0f
        }
        while (direction >= 180.0f) {
            direction -= 360.0f
        }
        return last + progress * direction
    }

    /**
     * Render a fake [HolographicEntity]. In order to render a fully opaque entity, set [holographicness] to `1F`.
     */
    fun <T : EntityLivingBase> renderHolographicEntity(
        holographicEntity: HolographicEntity<T>,
        partialTicks: Float,
        holographicness: Float = 0.3f
    ) {
        val renderManager = Minecraft.getMinecraft().renderManager
        val renderer = renderManager.getEntityRenderObject<EntityLivingBase>(holographicEntity.entity)
        renderer as RendererLivingEntity<T>
        renderer as AccessorRendererLivingEntity<T>

        renderer.setRenderOutlines(false)
        if (!renderer.bindEntityTexture_skyhanni(holographicEntity.entity))
            return

        GlStateManager.pushMatrix()
        val viewerPosition = getViewerPos(partialTicks)
        val mobPosition = holographicEntity.interpolatedPosition(partialTicks)
        val renderingOffset = mobPosition.subtract(viewerPosition)
        renderingOffset.applyTranslationToGL()
        GlStateManager.disableCull()
        GlStateManager.enableRescaleNormal()
        GlStateManager.scale(-1f, -1f, 1f)
        GlStateManager.translate(0F, -1.5078125f, 0f)
        val limbSwing: Float = 0F
        val limbSwingAmount: Float = 0F
        val ageInTicks: Float = 1_000_000.toFloat()
        val netHeadYaw: Float = holographicEntity.interpolatedYaw(partialTicks)
        val headPitch: Float = 0F
        val scaleFactor: Float = 0.0625f
        renderer.setBrightness_skyhanni(holographicEntity.entity, 0f, true)
        GlStateManager.color(1.0f, 1.0f, 1.0f, holographicness)
        GlStateManager.depthMask(false)
        GlStateManager.enableBlend()
        GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
        GlStateManager.alphaFunc(GL11.GL_GREATER, 1 / 255F)

        GlStateManager.enableTexture2D()
        renderer.mainModel.isChild = holographicEntity.isChild
        renderer.mainModel.setRotationAngles(
            limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scaleFactor, holographicEntity.entity
        )
        renderer.mainModel.render(
            holographicEntity.entity,
            limbSwing,
            limbSwingAmount,
            ageInTicks,
            netHeadYaw,
            headPitch,
            scaleFactor
        )
        GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1f)
        GlStateManager.color(1f, 1f, 1f, 1f)
        GlStateManager.depthMask(true)
        GlStateManager.disableBlend()
        GlStateManager.popMatrix()
    }

}