Android switchable features, android night vision
Achieved with cry engine two™️
This commit is contained in:
parent
ca70916331
commit
71f3ad133e
@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability;
|
||||
import ru.dbotthepony.mc.otm.capability.drive.DrivePool;
|
||||
import ru.dbotthepony.mc.otm.client.AndroidMenuKeyMapping;
|
||||
import ru.dbotthepony.mc.otm.client.ClientEventHandlerKt;
|
||||
import ru.dbotthepony.mc.otm.client.MatteryGUI;
|
||||
import ru.dbotthepony.mc.otm.client.model.GravitationStabilizerModel;
|
||||
@ -105,6 +106,7 @@ public final class OverdriveThatMatters {
|
||||
EVENT_BUS.addListener(EventPriority.LOWEST, GlobalEventHandlerKt::onServerTick);
|
||||
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayerCapability.Companion::onPlayerTick);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayerCapability.Companion::isMobEffectApplicable);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayerCapability.Companion::onHurtEvent);
|
||||
EVENT_BUS.addGenericListener(Entity.class, EventPriority.NORMAL, MatteryPlayerCapability.Companion::onAttachCapabilityEvent);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayerCapability.Companion::onPlayerChangeDimensionEvent);
|
||||
@ -162,6 +164,9 @@ public final class OverdriveThatMatters {
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, TooltipsKt::tooltipEvent);
|
||||
}
|
||||
|
||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(EventPriority.NORMAL, AndroidMenuKeyMapping.INSTANCE::register);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, AndroidMenuKeyMapping.INSTANCE::onRenderGuiEvent);
|
||||
|
||||
event.enqueueWork(GlobalEventHandlerKt::recordClientThread);
|
||||
|
||||
TritaniumArmorModel.register();
|
||||
|
@ -82,7 +82,7 @@ object ServerConfig {
|
||||
init {
|
||||
specBuilder.comment("Serverside config, holds shared values that are required to be read by both client and server.").push("server")
|
||||
|
||||
specBuilder.comment("Energy batteries balance values").push("energy_batteries")
|
||||
specBuilder.comment("Energy batteries balance values").push("energyBatteries")
|
||||
}
|
||||
|
||||
val BATTERY_CRUDE = batteryValues(MNames.BATTERY_CRUDE, ImpreciseFraction(100_000), ImpreciseFraction(160), ImpreciseFraction(40), ImpreciseFraction(80_000))
|
||||
@ -99,7 +99,7 @@ object ServerConfig {
|
||||
init {
|
||||
specBuilder.pop()
|
||||
|
||||
specBuilder.comment("Matter capacitors and pattern drives balance values").push("matter_capacitors_and_drives")
|
||||
specBuilder.comment("Matter capacitors and pattern drives balance values").push("matterCapacitorsAndDrives")
|
||||
}
|
||||
|
||||
val MATTER_CAPACITOR_BASIC by specBuilder.defineImpreciseFraction(MNames.MATTER_CAPACITOR_BASIC, ImpreciseFraction(2), minimum = ImpreciseFraction.ONE_TENTH)
|
||||
@ -131,16 +131,18 @@ object ServerConfig {
|
||||
init {
|
||||
specBuilder.pop()
|
||||
|
||||
specBuilder.comment("Tweaking of android players").push("android_player")
|
||||
specBuilder.comment("Tweaking of android players").push("androidPlayer")
|
||||
}
|
||||
|
||||
val ANDROID_ENERGY_PER_HUNGER_POINT by specBuilder.defineImpreciseFraction("energy_per_hunger", ImpreciseFraction(1000), ImpreciseFraction.ZERO)
|
||||
val ANDROID_ENERGY_PER_HUNGER_POINT by specBuilder.defineImpreciseFraction("energyPerHunger", ImpreciseFraction(1000), ImpreciseFraction.ZERO)
|
||||
val ANDROID_MAX_ENERGY by specBuilder.comment("Internal battery of every android has this much storage").defineImpreciseFraction("capacity", ImpreciseFraction(80_000), ImpreciseFraction.ZERO)
|
||||
|
||||
val NIGHT_VISION_POWER_DRAW by specBuilder.defineImpreciseFraction("nightVisionPowerDraw", ImpreciseFraction(8), ImpreciseFraction.ZERO)
|
||||
|
||||
init {
|
||||
specBuilder.pop()
|
||||
|
||||
specBuilder.comment("Tweaking of exosuits").push("exosuit_player")
|
||||
specBuilder.comment("Tweaking of exosuits").push("exosuitPlayer")
|
||||
}
|
||||
|
||||
val INFINITE_EXOSUIT_UPGRADES: Boolean by specBuilder.comment("Allows to apply the same upgrade over and over again.", "Obviously completely breaks balance.").define("infinite_upgrades", false)
|
||||
|
@ -18,6 +18,11 @@ typealias ResearchCallback = ((research: AndroidResearch, feature: AndroidFeatur
|
||||
@Suppress("unused")
|
||||
class AndroidResearchBuilder(
|
||||
var experience: Int = 0,
|
||||
var name: Component? = null,
|
||||
var customDescription: MutableList<Component>? = null,
|
||||
var hasDescription: Boolean = false,
|
||||
var skinIcon: SkinElement? = null,
|
||||
var iconText: Component? = null,
|
||||
) {
|
||||
private val items = ArrayList<ItemStack>()
|
||||
private val prerequisites = ArrayList<Pair<() -> ResourceLocation, Boolean>>()
|
||||
@ -38,12 +43,6 @@ class AndroidResearchBuilder(
|
||||
val callbackResearched: ResearchCallback?
|
||||
)
|
||||
|
||||
var name: Component? = null
|
||||
var description: MutableList<Component>? = null
|
||||
var hasDescription = false
|
||||
var skinIcon: SkinElement? = null
|
||||
var iconText: Component? = null
|
||||
|
||||
fun withIconText(icon: Component?): AndroidResearchBuilder {
|
||||
this.iconText = icon
|
||||
return this
|
||||
@ -58,7 +57,7 @@ class AndroidResearchBuilder(
|
||||
|
||||
fun withDescription(): AndroidResearchBuilder {
|
||||
this.hasDescription = true
|
||||
this.description = null
|
||||
this.customDescription = null
|
||||
return this
|
||||
}
|
||||
|
||||
@ -68,12 +67,12 @@ class AndroidResearchBuilder(
|
||||
}
|
||||
|
||||
fun withDescription(vararg description: Component): AndroidResearchBuilder {
|
||||
this.description = description.toMutableList()
|
||||
this.customDescription = description.toMutableList()
|
||||
return this
|
||||
}
|
||||
|
||||
fun withDescription(description: List<Component>): AndroidResearchBuilder {
|
||||
this.description = ArrayList<Component>(description.size).also { it.addAll(description) }
|
||||
this.customDescription = ArrayList<Component>(description.size).also { it.addAll(description) }
|
||||
return this
|
||||
}
|
||||
|
||||
@ -155,7 +154,7 @@ class AndroidResearchBuilder(
|
||||
val experience = this.experience
|
||||
|
||||
val name = name?.copy()
|
||||
val description = description?.let {
|
||||
val description = customDescription?.let {
|
||||
val builder = ImmutableList.builder<Component>()
|
||||
|
||||
for (component in it) {
|
||||
|
@ -0,0 +1,37 @@
|
||||
package ru.dbotthepony.mc.otm.android
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.core.set
|
||||
|
||||
abstract class AndroidSwitchableFeature(type: AndroidFeatureType<*>, android: MatteryPlayerCapability) : AndroidFeature(type, android) {
|
||||
var isActive by synchronizer.bool(setter = setter@{ value, access, setByRemote ->
|
||||
if (value != access.read()) {
|
||||
access.write(value)
|
||||
|
||||
if (!setByRemote) {
|
||||
if (value) {
|
||||
applyModifiers()
|
||||
} else {
|
||||
removeModifiers()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
open val allowToSwitchByPlayer: Boolean get() = true
|
||||
|
||||
abstract fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float)
|
||||
|
||||
override fun serializeNBT(): CompoundTag {
|
||||
return super.serializeNBT().also {
|
||||
it["isActive"] = isActive
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: CompoundTag) {
|
||||
super.deserializeNBT(nbt)
|
||||
isActive = nbt.getBoolean("isActive")
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package ru.dbotthepony.mc.otm.android.feature
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.world.effect.MobEffectInstance
|
||||
import net.minecraft.world.effect.MobEffects
|
||||
import ru.dbotthepony.mc.otm.ServerConfig
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
|
||||
import ru.dbotthepony.mc.otm.capability.extractEnergyInnerExact
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidResearch
|
||||
|
||||
class NightVisionFeature(android: MatteryPlayerCapability) : AndroidSwitchableFeature(AndroidFeatures.NIGHT_VISION, android) {
|
||||
override fun tickServer() {
|
||||
if (isActive) {
|
||||
val effect = android.ply.activeEffectsMap[MobEffects.NIGHT_VISION]
|
||||
|
||||
if ((effect == null || effect.duration < 220) && android.androidEnergy.extractEnergyInnerExact(ServerConfig.NIGHT_VISION_POWER_DRAW, true).isPositive) {
|
||||
android.ply.addEffect(MobEffectInstance(MobEffects.NIGHT_VISION, 220))
|
||||
android.androidEnergy.extractEnergyInner(ServerConfig.NIGHT_VISION_POWER_DRAW, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeModifiers() {
|
||||
val effect = android.ply.activeEffectsMap[MobEffects.NIGHT_VISION]
|
||||
|
||||
if (effect != null && effect.duration <= 222) {
|
||||
android.ply.removeEffect(MobEffects.NIGHT_VISION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderIcon(stack: PoseStack, x: Float, y: Float, width: Float, height: Float) {
|
||||
AndroidResearch.ICON_NIGHT_VISION.render(stack, x, y, width, height)
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import net.minecraftforge.event.entity.living.LivingDeathEvent
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent
|
||||
import net.minecraftforge.event.entity.living.LivingSpawnEvent
|
||||
import net.minecraftforge.event.entity.living.MobEffectEvent
|
||||
import net.minecraftforge.event.entity.player.EntityItemPickupEvent
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent
|
||||
import net.minecraftforge.eventbus.api.Event
|
||||
@ -49,6 +50,7 @@ import ru.dbotthepony.mc.otm.network.*
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
import java.util.*
|
||||
import java.util.stream.Stream
|
||||
import kotlin.collections.ArrayDeque
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@ -170,7 +172,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
private val deathLog = ArrayDeque<Pair<Int, Component>>()
|
||||
|
||||
private val features = IdentityHashMap<AndroidFeatureType<*>, AndroidFeature>()
|
||||
private val featureMap = IdentityHashMap<AndroidFeatureType<*>, AndroidFeature>()
|
||||
private val networkQueue = ArrayList<Any>()
|
||||
private val queuedTicks = ArrayList<Runnable>()
|
||||
private var tickedOnce = false
|
||||
@ -252,7 +254,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
val copy = ArrayList<AndroidFeature>(features.values.size).also { it.addAll(features.values) }
|
||||
val copy = ArrayList<AndroidFeature>(featureMap.values.size).also { it.addAll(featureMap.values) }
|
||||
|
||||
for (feature in copy) {
|
||||
removeFeature(feature.type)
|
||||
@ -268,9 +270,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
} as T
|
||||
}
|
||||
|
||||
val features: Stream<out AndroidFeature> get() = featureMap.values.stream()
|
||||
|
||||
private fun addFeature(feature: AndroidFeature): Boolean {
|
||||
if (features.containsKey(feature.type)) return false
|
||||
features[feature.type] = feature
|
||||
if (featureMap.containsKey(feature.type)) return false
|
||||
featureMap[feature.type] = feature
|
||||
|
||||
if (!ply.level.isClientSide) {
|
||||
queuedTicks.add(feature::applyModifiers)
|
||||
@ -290,12 +294,12 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
|
||||
val get = features[feature]
|
||||
val get = featureMap[feature]
|
||||
if (get != null) return get as T
|
||||
|
||||
val factory = feature.create(this)
|
||||
|
||||
features[feature] = factory
|
||||
featureMap[feature] = factory
|
||||
|
||||
if (!ply.level.isClientSide) {
|
||||
queuedTicks.add(factory::applyModifiers)
|
||||
@ -314,7 +318,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
|
||||
fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
|
||||
val removed = features.remove(feature)
|
||||
val removed = featureMap.remove(feature)
|
||||
|
||||
if (removed != null) {
|
||||
if (!ply.level.isClientSide) {
|
||||
@ -332,21 +336,21 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
|
||||
fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
|
||||
return features.containsKey(feature)
|
||||
return featureMap.containsKey(feature)
|
||||
}
|
||||
|
||||
fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
|
||||
val get = features[feature] ?: return false
|
||||
val get = featureMap[feature] ?: return false
|
||||
return get.level >= level
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
|
||||
return features[feature] as T?
|
||||
return featureMap[feature] as T?
|
||||
}
|
||||
|
||||
fun onHurt(event: LivingHurtEvent) {
|
||||
for (feature in features.values) {
|
||||
for (feature in featureMap.values) {
|
||||
feature.onHurt(event)
|
||||
}
|
||||
}
|
||||
@ -401,7 +405,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
val featureList = ListTag()
|
||||
val researchList = ListTag()
|
||||
|
||||
for (feature in features.values) {
|
||||
for (feature in featureMap.values) {
|
||||
featureList.add(feature.serializeNBT().also {
|
||||
it["id"] = feature.type.registryName!!.toString()
|
||||
})
|
||||
@ -449,7 +453,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
tag.map("androidEnergy", androidEnergy::deserializeNBT)
|
||||
|
||||
features.clear()
|
||||
featureMap.clear()
|
||||
research.clear()
|
||||
|
||||
for (featureTag in tag.getCompoundList("features")) {
|
||||
@ -512,7 +516,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
|
||||
if (isAndroid) {
|
||||
for (feature in features.values) {
|
||||
for (feature in featureMap.values) {
|
||||
feature.tickClient()
|
||||
}
|
||||
}
|
||||
@ -582,7 +586,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
stats.setExhaustion(stats.exhaustionLevel - (extracted / ServerConfig.ANDROID_ENERGY_PER_HUNGER_POINT).toFloat() * 4f)
|
||||
}
|
||||
|
||||
for (feature in features.values) {
|
||||
for (feature in featureMap.values) {
|
||||
feature.tickServer()
|
||||
}
|
||||
}
|
||||
@ -600,7 +604,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
instance.invalidateNetwork()
|
||||
}
|
||||
|
||||
for (feature in features.values) {
|
||||
for (feature in featureMap.values) {
|
||||
feature.invalidateNetwork()
|
||||
}
|
||||
}
|
||||
@ -629,7 +633,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
for (instance in features.values) {
|
||||
for (instance in featureMap.values) {
|
||||
val featurePayload = instance.collectNetworkPayload()
|
||||
|
||||
if (featurePayload != null) {
|
||||
@ -683,6 +687,14 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
fun isMobEffectApplicable(event: MobEffectEvent.Applicable) {
|
||||
event.entity.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
|
||||
if (it.isAndroid && ForgeRegistries.MOB_EFFECTS.tags()?.getTag(ANDROID_IMMUNE_EFFECTS)?.stream()?.anyMatch { it == event.effectInstance.effect } == true) {
|
||||
event.result = Event.Result.DENY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onHurtEvent(event: LivingHurtEvent) {
|
||||
if (event.isCanceled) {
|
||||
return
|
||||
|
@ -0,0 +1,161 @@
|
||||
package ru.dbotthepony.mc.otm.client
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.KeyMapping
|
||||
import net.minecraftforge.client.event.RegisterKeyMappingsEvent
|
||||
import net.minecraftforge.client.event.RenderGuiEvent
|
||||
import net.minecraftforge.client.settings.KeyConflictContext
|
||||
import org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED
|
||||
import org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||
import ru.dbotthepony.mc.otm.client.render.drawArc
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.SwitchAndroidFeaturePacket
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.acos
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
// TODO: forge's KeyMapping patch (ALL and MAP maps) is not thread safe... should we be worried?
|
||||
object AndroidMenuKeyMapping : KeyMapping("key.otm.android_menu", KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM.getOrCreate(InputConstants.KEY_F), OverdriveThatMatters.MOD_ID) {
|
||||
private var grabbedInput = false
|
||||
private var selectedFeature: AndroidSwitchableFeature? = null
|
||||
|
||||
override fun setDown(isDown: Boolean) {
|
||||
val old = this.isDown
|
||||
super.setDown(isDown)
|
||||
|
||||
if (old != isDown) {
|
||||
if (
|
||||
minecraft.screen == null &&
|
||||
isDown &&
|
||||
minecraft.player?.matteryPlayer?.isAndroid == true &&
|
||||
minecraft.player?.matteryPlayer?.features?.anyMatch { it is AndroidSwitchableFeature && it.allowToSwitchByPlayer } == true
|
||||
) {
|
||||
grabbedInput = true
|
||||
minecraft.mouseHandler.releaseMouse()
|
||||
} else if (!isDown && grabbedInput) {
|
||||
grabbedInput = false
|
||||
|
||||
if (minecraft.screen == null) {
|
||||
minecraft.mouseHandler.grabMouse()
|
||||
|
||||
val selectedFeature = selectedFeature
|
||||
if (selectedFeature != null) {
|
||||
MatteryPlayerNetworkChannel.sendToServer(SwitchAndroidFeaturePacket(selectedFeature.type, !selectedFeature.isActive))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedFeature = null
|
||||
}
|
||||
}
|
||||
|
||||
fun onRenderGuiEvent(event: RenderGuiEvent.Post) {
|
||||
if (!grabbedInput) {
|
||||
return
|
||||
}
|
||||
|
||||
val features = minecraft.player?.matteryPlayer?.features?.filter { it is AndroidSwitchableFeature && it.allowToSwitchByPlayer }?.collect(Collectors.toList()) as MutableList<AndroidSwitchableFeature>? ?: return
|
||||
|
||||
if (features.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
RenderSystem.setShaderColor(0f, 0f, 0f, 0.75f)
|
||||
|
||||
val size = minecraft.window.guiScaledHeight.coerceAtMost(minecraft.window.guiScaledWidth).toFloat() * 0.35f
|
||||
|
||||
drawArc(
|
||||
event.poseStack,
|
||||
minecraft.window.guiScaledWidth / 2f,
|
||||
minecraft.window.guiScaledHeight / 2f,
|
||||
size,
|
||||
size * 0.3f
|
||||
)
|
||||
|
||||
RenderSystem.setShaderColor(85 / 255f, 197 / 255f, 255 / 255f, 0.5f)
|
||||
|
||||
val degreePerSlice = PI * 2.0 / features.size
|
||||
|
||||
var (mouseX, mouseY) = mousePos
|
||||
|
||||
mouseX -= minecraft.window.width / 2.0
|
||||
mouseY -= minecraft.window.height / 2.0
|
||||
|
||||
//font.drawAligned(event.poseStack, mouseX.toString(), TextAlign.CENTER_CENTER, 100f, 40f, RGBAColor.WHITE)
|
||||
//font.drawAligned(event.poseStack, mouseY.toString(), TextAlign.CENTER_CENTER, 100f, 60f, RGBAColor.WHITE)
|
||||
|
||||
val length = (mouseX * mouseX + mouseY * mouseY).pow(0.5)
|
||||
|
||||
if ((length / minecraft.window.guiScale) in (size * 0.3f) .. size) {
|
||||
var deg = acos(mouseX / length)
|
||||
|
||||
// opengl
|
||||
// lower values are at bottom of screen
|
||||
if (mouseY > 0.0) {
|
||||
deg = -deg
|
||||
}
|
||||
|
||||
if (deg < 0.0) {
|
||||
deg += PI * 2
|
||||
}
|
||||
|
||||
//font.drawAligned(event.poseStack, deg.toString(), TextAlign.CENTER_CENTER, 100f, 20f, RGBAColor.WHITE)
|
||||
|
||||
val index = (deg / degreePerSlice).toInt()
|
||||
selectedFeature = features[index]
|
||||
|
||||
//font.drawAligned(event.poseStack, index.toString(), TextAlign.CENTER_CENTER, 100f, 80f, RGBAColor.WHITE)
|
||||
|
||||
RenderSystem.setShaderColor(85 / 255f, 197 / 255f, 255 / 255f, 0.3f)
|
||||
|
||||
drawArc(
|
||||
event.poseStack,
|
||||
minecraft.window.guiScaledWidth / 2f,
|
||||
minecraft.window.guiScaledHeight / 2f,
|
||||
size * 1.2f,
|
||||
size * 0.4f,
|
||||
|
||||
startDegree = index * degreePerSlice,
|
||||
endDegree = (index + 1) * degreePerSlice
|
||||
)
|
||||
|
||||
//font.drawAligned(event.poseStack, (index * degreePerSlice).toString(), TextAlign.CENTER_CENTER, 100f, 100f, RGBAColor.WHITE)
|
||||
} else {
|
||||
selectedFeature = null
|
||||
}
|
||||
|
||||
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
|
||||
|
||||
val iconSize = size * 0.25f
|
||||
|
||||
event.poseStack.pushPose()
|
||||
event.poseStack.translate(minecraft.window.guiScaledWidth.toDouble() / 2f, minecraft.window.guiScaledHeight.toDouble() / 2f, 0.0)
|
||||
|
||||
if (features.size == 1) {
|
||||
val feature = features.first()
|
||||
|
||||
feature.renderIcon(event.poseStack, -iconSize / 2f, -size * 0.7f - iconSize / 2f, iconSize, iconSize)
|
||||
} else {
|
||||
for ((index, feature) in features.withIndex()) {
|
||||
val sin = sin((index + 0.5) * degreePerSlice).toFloat()
|
||||
val cos = cos((index + 0.5) * degreePerSlice).toFloat()
|
||||
|
||||
feature.renderIcon(event.poseStack, -iconSize / 2f + size * 0.7f * cos, -size * 0.7f * sin - iconSize / 2f, iconSize, iconSize)
|
||||
}
|
||||
}
|
||||
|
||||
event.poseStack.popPose()
|
||||
}
|
||||
|
||||
fun register(event: RegisterKeyMappingsEvent) {
|
||||
event.register(this)
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import net.minecraft.client.gui.Font
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import org.lwjgl.glfw.GLFW.GLFW_CURSOR
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.DoubleBuffer
|
||||
@ -35,6 +36,28 @@ fun setMousePosScaled(x: Float, y: Float) {
|
||||
private val cursorXPosBuf = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
|
||||
private val cursorYPosBuf = ByteBuffer.allocateDirect(8).also { it.order(ByteOrder.LITTLE_ENDIAN) }.asDoubleBuffer()
|
||||
|
||||
data class ScaledMousePos(val x: Double, val y: Double) {
|
||||
fun set() {
|
||||
setMousePos(x * minecraft.window.guiScale, y * minecraft.window.guiScale)
|
||||
}
|
||||
|
||||
fun move(x: Double = 0.0, y: Double = 0.0) {
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, this.x * minecraft.window.guiScale + x, this.y * minecraft.window.guiScale + y)
|
||||
}
|
||||
|
||||
fun move(x: Float = 0.0f, y: Float = 0.0f) {
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, this.x * minecraft.window.guiScale + x, this.y * minecraft.window.guiScale + y)
|
||||
}
|
||||
|
||||
fun moveScaled(x: Double = 0.0, y: Double = 0.0) {
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, this.x * minecraft.window.guiScale + x * minecraft.window.guiScale, this.y * minecraft.window.guiScale + y * minecraft.window.guiScale)
|
||||
}
|
||||
|
||||
fun moveScaled(x: Float = 0.0f, y: Float = 0.0f) {
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, this.x * minecraft.window.guiScale + x * minecraft.window.guiScale, this.y * minecraft.window.guiScale + y * minecraft.window.guiScale)
|
||||
}
|
||||
}
|
||||
|
||||
data class MousePos(val x: Double, val y: Double) {
|
||||
fun set() {
|
||||
setMousePos(x, y)
|
||||
@ -69,6 +92,18 @@ val mousePos: MousePos get() {
|
||||
return MousePos(cursorXPosBuf.get(), cursorYPosBuf.get())
|
||||
}
|
||||
|
||||
val scaledMousePos: ScaledMousePos get() {
|
||||
cursorXPosBuf.position(0)
|
||||
cursorYPosBuf.position(0)
|
||||
|
||||
GLFW.glfwGetCursorPos(minecraft.window.window, cursorXPosBuf, cursorYPosBuf)
|
||||
|
||||
cursorXPosBuf.position(0)
|
||||
cursorYPosBuf.position(0)
|
||||
|
||||
return ScaledMousePos(cursorXPosBuf.get() / minecraft.window.guiScale, cursorYPosBuf.get() / minecraft.window.guiScale)
|
||||
}
|
||||
|
||||
fun moveMousePos(x: Double = 0.0, y: Double = 0.0) {
|
||||
val (currentX, currentY) = mousePos
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, currentX + x, currentY + y)
|
||||
@ -88,3 +123,7 @@ fun moveMousePosScaled(x: Float = 0.0f, y: Float = 0.0f) {
|
||||
val (currentX, currentY) = mousePos
|
||||
GLFW.glfwSetCursorPos(minecraft.window.window, currentX + x * minecraft.window.guiScale, currentY + y * minecraft.window.guiScale)
|
||||
}
|
||||
|
||||
var mouseInputMode: Int
|
||||
get() = GLFW.glfwGetInputMode(minecraft.window.window, GLFW_CURSOR)
|
||||
set(value) { GLFW.glfwSetInputMode(minecraft.window.window, GLFW_CURSOR, value) }
|
||||
|
@ -97,7 +97,7 @@ object MatteryGUI {
|
||||
|
||||
val deathLog = ArrayList<Pair<Int, Component>>()
|
||||
|
||||
fun showIteration(event: RenderGuiEvent.Post) {
|
||||
private fun showIteration(event: RenderGuiEvent.Post) {
|
||||
if (minecraft.player?.matteryPlayer?.isAndroid != true) {
|
||||
return
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import org.lwjgl.opengl.GL11.GL_ALWAYS
|
||||
import org.lwjgl.opengl.GL11.GL_LESS
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.core.RGBAColor
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.acos
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
@ -563,3 +564,93 @@ fun clearDepth(stack: PoseStack, x: Float, y: Float, width: Float, height: Float
|
||||
RenderSystem.setShader { oldShader }
|
||||
}
|
||||
}
|
||||
|
||||
fun drawArc(
|
||||
stack: PoseStack,
|
||||
x: Float,
|
||||
y: Float,
|
||||
outerRadius: Float,
|
||||
innerRadius: Float = 0f,
|
||||
startDegree: Double = 0.0,
|
||||
endDegree: Double = PI * 2.0,
|
||||
steps: Int = (outerRadius / 8.0 * (endDegree - startDegree)).roundToInt().coerceAtLeast(12),
|
||||
alignAtCenter: Boolean = true
|
||||
) = drawArc(stack.last().pose(), x, y, outerRadius, innerRadius, startDegree, endDegree, steps, alignAtCenter)
|
||||
|
||||
fun drawArc(
|
||||
matrix: Matrix4f,
|
||||
x: Float,
|
||||
y: Float,
|
||||
outerRadius: Float,
|
||||
innerRadius: Float = 0f,
|
||||
startDegree: Double = 0.0,
|
||||
endDegree: Double = PI * 2.0,
|
||||
steps: Int = (outerRadius / 8.0 * (endDegree - startDegree)).roundToInt().coerceAtLeast(12),
|
||||
alignAtCenter: Boolean = true
|
||||
) {
|
||||
require(startDegree < endDegree) { "Invalid arc degree range: $startDegree - $endDegree" }
|
||||
require(steps >= 0) { "Invalid amount of arc steps: $steps" }
|
||||
require(innerRadius >= 0f) { "Invalid inner radius of arc: $innerRadius" }
|
||||
|
||||
RenderSystem.setShader(GameRenderer::getPositionShader)
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.defaultBlendFunc()
|
||||
RenderSystem.depthFunc(GL_ALWAYS)
|
||||
|
||||
@Suppress("name_shadowing")
|
||||
var x = x
|
||||
|
||||
@Suppress("name_shadowing")
|
||||
var y = y
|
||||
|
||||
@Suppress("name_shadowing")
|
||||
val startDegree = startDegree + PI / 2.0
|
||||
|
||||
@Suppress("name_shadowing")
|
||||
val endDegree = endDegree + PI / 2.0
|
||||
|
||||
if (!alignAtCenter) {
|
||||
x += outerRadius
|
||||
y += outerRadius
|
||||
}
|
||||
|
||||
val builder = tesselator.builder
|
||||
|
||||
if (innerRadius == 0f) {
|
||||
if (steps >= 1) {
|
||||
val singleStep = (endDegree - startDegree) / steps
|
||||
|
||||
builder.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION)
|
||||
|
||||
builder.vertex(matrix, x, y, zLevel).endVertex()
|
||||
|
||||
for (i in 0 .. steps) {
|
||||
val sin = sin(startDegree + i * singleStep).toFloat()
|
||||
val cos = cos(startDegree + i * singleStep).toFloat()
|
||||
|
||||
builder.vertex(matrix, x + outerRadius * sin, y + cos * outerRadius, zLevel).endVertex()
|
||||
}
|
||||
|
||||
BufferUploader.drawWithShader(builder.end())
|
||||
}
|
||||
} else {
|
||||
val singleStep = (endDegree - startDegree) / (steps + 1)
|
||||
|
||||
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION)
|
||||
|
||||
for (i in 0 .. steps) {
|
||||
val sin = sin(startDegree + i * singleStep).toFloat()
|
||||
val cos = cos(startDegree + i * singleStep).toFloat()
|
||||
|
||||
val sin2 = sin(startDegree + (i + 1) * singleStep).toFloat()
|
||||
val cos2 = cos(startDegree + (i + 1) * singleStep).toFloat()
|
||||
|
||||
builder.vertex(matrix, x + outerRadius * sin, y + cos * outerRadius, zLevel).endVertex()
|
||||
builder.vertex(matrix, x + outerRadius * sin2, y + cos2 * outerRadius, zLevel).endVertex()
|
||||
builder.vertex(matrix, x + innerRadius * sin2, y + cos2 * innerRadius, zLevel).endVertex()
|
||||
builder.vertex(matrix, x + innerRadius * sin, y + cos * innerRadius, zLevel).endVertex()
|
||||
}
|
||||
|
||||
BufferUploader.drawWithShader(builder.end())
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,10 @@ abstract class MatteryNetworkChannel(val version: String, val name: String) {
|
||||
handler: BiConsumer<T, Supplier<NetworkEvent.Context>>,
|
||||
direction: NetworkDirection? = null
|
||||
) {
|
||||
if (nextNetworkPacketID >= 256) {
|
||||
throw IndexOutOfBoundsException("Network message ID overflow!")
|
||||
}
|
||||
|
||||
@Suppress("INACCESSIBLE_TYPE")
|
||||
channel.registerMessage(nextNetworkPacketID++, packetClass, writer, reader, handler, if (direction == null) Optional.empty() else Optional.of(direction))
|
||||
}
|
||||
|
@ -12,10 +12,12 @@ import ru.dbotthepony.mc.otm.android.AndroidFeature
|
||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearch
|
||||
import ru.dbotthepony.mc.otm.android.AndroidResearchType
|
||||
import ru.dbotthepony.mc.otm.android.AndroidSwitchableFeature
|
||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||
import ru.dbotthepony.mc.otm.client.MatteryGUI
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.menu.AndroidStationMenu
|
||||
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
|
||||
import ru.dbotthepony.mc.otm.registry.MRegistry
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.OutputStream
|
||||
@ -320,6 +322,37 @@ object ExoSuitMenuClose : MatteryPacket {
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchAndroidFeaturePacket(val type: AndroidFeatureType<*>, val newState: Boolean) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(MRegistry.ANDROID_FEATURES.getID(type))
|
||||
buff.writeBoolean(newState)
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.packetHandled = true
|
||||
|
||||
context.enqueueWork {
|
||||
val matteryPlayer = context.sender?.matteryPlayer ?: return@enqueueWork
|
||||
|
||||
if (!matteryPlayer.isAndroid) {
|
||||
return@enqueueWork
|
||||
}
|
||||
|
||||
val feature = matteryPlayer.getFeature(type) ?: return@enqueueWork
|
||||
|
||||
if (feature is AndroidSwitchableFeature && feature.allowToSwitchByPlayer) {
|
||||
feature.isActive = newState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buff: FriendlyByteBuf): SwitchAndroidFeaturePacket {
|
||||
return SwitchAndroidFeaturePacket(MRegistry.ANDROID_FEATURES.getValue(buff.readInt()) ?: AndroidFeatures.AIR_BAGS, buff.readBoolean())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
||||
version = "1",
|
||||
name = "player"
|
||||
@ -337,7 +370,10 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
|
||||
add(ExoSuitCarriedPacket::class, ExoSuitCarriedPacket.Companion::read, PLAY_TO_CLIENT)
|
||||
add(ExoSuitSlotPacket::class, ExoSuitSlotPacket.Companion::read, PLAY_TO_CLIENT)
|
||||
add(ExoSuitMenuInitPacket::class, ExoSuitMenuInitPacket.Companion::read, PLAY_TO_CLIENT)
|
||||
|
||||
add(ExoSuitMenuOpen::class, { ExoSuitMenuOpen }, PLAY_TO_SERVER)
|
||||
add(ExoSuitMenuClose::class, { ExoSuitMenuClose }, PLAY_TO_SERVER)
|
||||
|
||||
add(SwitchAndroidFeaturePacket::class, SwitchAndroidFeaturePacket.Companion::read, PLAY_TO_SERVER)
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ object AndroidFeatures {
|
||||
val NANOBOTS_REGENERATION: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_REGENERATION) { AndroidFeatureType(::NanobotsRegeneration) }
|
||||
val NANOBOTS_ARMOR: AndroidFeatureType<*> by registry.register(MNames.NANOBOTS_ARMOR) { AndroidFeatureType(::NanobotsArmor) }
|
||||
val EXTENDED_REACH: AndroidFeatureType<*> by registry.register(MNames.EXTENDED_REACH) { AndroidFeatureType(::ExtendedReach) }
|
||||
val NIGHT_VISION: AndroidFeatureType<*> by registry.register(MNames.NIGHT_VISION) { AndroidFeatureType(::NightVisionFeature) }
|
||||
|
||||
internal fun register(bus: IEventBus) {
|
||||
registry.register(bus)
|
||||
|
@ -118,6 +118,15 @@ object AndroidResearch {
|
||||
.build()
|
||||
}
|
||||
|
||||
val NIGHT_VISION: AndroidResearchType<*> by registry.register(MNames.NIGHT_VISION) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(40)
|
||||
.withDescription()
|
||||
.withIcon(ICON_NIGHT_VISION)
|
||||
.addFeatureResult(AndroidFeatures.NIGHT_VISION)
|
||||
.build()
|
||||
}
|
||||
|
||||
val NANOBOTS: AndroidResearchType<*> by registry.register(MNames.NANOBOTS) {
|
||||
AndroidResearchBuilder()
|
||||
.withExperience(15)
|
||||
|
@ -207,6 +207,7 @@ object MNames {
|
||||
const val HYDRAULICS_OVERLOAD_3 = "hydraulics_overload_3"
|
||||
|
||||
const val EXTENDED_REACH = "extended_reach"
|
||||
const val NIGHT_VISION = "night_vision"
|
||||
const val IMPROVED_LIMBS = "improved_limbs"
|
||||
|
||||
// stats
|
||||
|
Loading…
Reference in New Issue
Block a user