Add docs to MatteryPlayerCapability
This commit is contained in:
parent
fd7619b144
commit
5c5c68742b
@ -79,15 +79,34 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
/**
|
||||
* For fields that need to be synchronized only to owning player
|
||||
*
|
||||
* Please mind if you really need to use this in your mod;
|
||||
* don't forget to specify field names when you add them to synchronizer.
|
||||
*
|
||||
* Even if other side does not have your field defined, both sides will negotiate
|
||||
* and figure out how to deal with this situation as long as you specify field name.
|
||||
*/
|
||||
val synchronizer = FieldSynchronizer()
|
||||
|
||||
/**
|
||||
* For fields that need to be synchronized to everyone
|
||||
*
|
||||
* Please mind if you really need to use this in your mod;
|
||||
* don't forget to specify field names when you add them to synchronizer.
|
||||
*
|
||||
* Even if other side does not have your field defined, both sides will negotiate
|
||||
* and figure out how to deal with this situation as long as you specify field name.
|
||||
*/
|
||||
val publicSynchronizer = FieldSynchronizer()
|
||||
|
||||
/**
|
||||
* Whenever player has Exopack
|
||||
*/
|
||||
var hasExoPack by publicSynchronizer.bool(name = "hasExoPack")
|
||||
|
||||
/**
|
||||
* Whenever to render Exopack on player
|
||||
*/
|
||||
var displayExoPack by publicSynchronizer.bool(true, name = "displayExoPack")
|
||||
|
||||
private val exoPackSlotModifierMap: MutableMap<UUID, Int> by synchronizer.Map(
|
||||
@ -100,6 +119,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
name = "exoPackSlotModifierMap"
|
||||
)
|
||||
|
||||
/**
|
||||
* Modifier map for Exopack slot counts
|
||||
*
|
||||
* If you want to properly extend Exopack suit capacity, add your value into this map
|
||||
*/
|
||||
val exoPackSlotModifier = UUIDIntModifiersMap(observer = observer@{
|
||||
if (ply !is ServerPlayer)
|
||||
return@observer
|
||||
@ -111,6 +135,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}, backingMap = this.exoPackSlotModifierMap)
|
||||
|
||||
/**
|
||||
* Current slot count of Exopack
|
||||
*
|
||||
* For properly affecting this value please look at [exoPackSlotModifier]
|
||||
*/
|
||||
var exoPackSlotCount by publicSynchronizer.int(setter = setter@{ value, access, _ ->
|
||||
require(value >= 0) { "Invalid slot count $value" }
|
||||
|
||||
@ -121,6 +150,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}, name = "exoPackSlotCount")
|
||||
|
||||
/**
|
||||
* Exopack container, which actually store items inside Exopack
|
||||
*/
|
||||
var exoPackContainer: MatteryContainer = PlayerMatteryContainer(0)
|
||||
private set(value) {
|
||||
_exoPackMenu = null
|
||||
@ -143,6 +175,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever Exopack has 3x3 crafting grid upgrade installed
|
||||
*/
|
||||
var isExoPackCraftingUpgraded by publicSynchronizer.bool(setter = setter@{ value, access, _ ->
|
||||
if (value != access.read()) {
|
||||
access.write(value)
|
||||
@ -156,6 +191,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* [ExoPackInventoryMenu] which player will see when pressing inventory key
|
||||
*/
|
||||
val exoPackMenu: ExoPackInventoryMenu
|
||||
get() {
|
||||
if (_exoPackMenu == null) {
|
||||
@ -166,6 +204,10 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
|
||||
private var shouldSendIteration = false
|
||||
|
||||
/**
|
||||
* Android' iteration counter (death counter, updating only when Android)
|
||||
*/
|
||||
var iteration = 0
|
||||
private set
|
||||
|
||||
@ -186,15 +228,41 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
// getting them unburied will be a very work intense task
|
||||
private val trackingPlayers = Reference2ObjectOpenHashMap<ServerPlayer, FieldSynchronizer.Endpoint>()
|
||||
|
||||
/**
|
||||
* This returns if player is an Android or will become one on death/sleep/etc
|
||||
*/
|
||||
val isEverAndroid: Boolean get() = isAndroid || willBecomeAndroid
|
||||
var lastJumpTicks = 14
|
||||
internal var lastJumpTicks = 14
|
||||
|
||||
/**
|
||||
* In-game ticks this player exists (server play time, excluding time while "dead"), used by various things across the mod, such as
|
||||
* "play time chance" loot condition
|
||||
*/
|
||||
var ticksIExist = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Whenever player should become an Android once transformation conditions are met (e.g. player dies or sleeps in bed)
|
||||
*/
|
||||
var willBecomeAndroid by publicSynchronizer.bool(name = "willBecomeAndroid")
|
||||
|
||||
/**
|
||||
* Whenever player is an Android
|
||||
*
|
||||
* In OTM itself, Android players are generally treated as something in between alive and undead, such as:
|
||||
* * They can't be healed using potions, can't receive regeneration buffs
|
||||
* * But they can be harmed using harm potion
|
||||
* * Can't be poisoned (with default datapack)
|
||||
* * CAN be withered (with default datapack)
|
||||
* * etc
|
||||
*
|
||||
* Android-immune (de)buffs are specified in `data/overdrive_that_matters/tags/mob_effect/android_immune_effects.json`
|
||||
*/
|
||||
var isAndroid by publicSynchronizer.bool(name = "isAndroid")
|
||||
|
||||
/**
|
||||
* [IMatteryEnergyStorage] instance, representing Android' battery charge
|
||||
*/
|
||||
val androidEnergy = AndroidPowerSource(ply, synchronizer, ServerConfig.ANDROID_MAX_ENERGY, ServerConfig.ANDROID_MAX_ENERGY)
|
||||
|
||||
fun invalidateNetworkState() {
|
||||
@ -214,12 +282,29 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags player to turn into Android
|
||||
*
|
||||
* This is used by Android Pill item in-game
|
||||
*
|
||||
* Does nothing if player is already an Android
|
||||
*/
|
||||
fun becomeAndroidSoft() {
|
||||
if (isAndroid || willBecomeAndroid) return
|
||||
willBecomeAndroid = true
|
||||
(ply as? ServerPlayer)?.displayClientMessage(TranslatableComponent("otm.pill.message").withStyle(ChatFormatting.GRAY), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally and instantly turns player into Android
|
||||
*
|
||||
* This is different than setting [isAndroid] because it setup some variables,
|
||||
* such as battery charge
|
||||
*
|
||||
* Does not kill the player
|
||||
*
|
||||
* Does nothing if player is already an Android
|
||||
*/
|
||||
fun becomeAndroid() {
|
||||
if (isAndroid) return
|
||||
|
||||
@ -236,6 +321,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [becomeAndroid] plus instantly kills the player
|
||||
*
|
||||
* Does nothing if player is already an Android
|
||||
*/
|
||||
fun becomeAndroidAndKill() {
|
||||
if (isAndroid) return
|
||||
|
||||
@ -243,6 +333,25 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
ply.hurt(MRegistry.DAMAGE_BECOME_ANDROID, ply.maxHealth * 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally and instantly turns player into "human"
|
||||
*
|
||||
* This is different than setting [isAndroid] because it setup some variables,
|
||||
* such as battery charge
|
||||
*
|
||||
* Does not kill the player
|
||||
*
|
||||
* Drops equipped battery BUT does NOT refund research, nor does remove features;
|
||||
* Once player becomes Android again, they will retain all features and research.
|
||||
*
|
||||
* If you need to also remove research and features, look at [obliviate]
|
||||
*
|
||||
* However, this does reset [iteration] and death log
|
||||
*
|
||||
* Does nothing if player is not an Android
|
||||
*
|
||||
* Unsets [willBecomeAndroid] flag if it is true
|
||||
*/
|
||||
fun becomeHumane() {
|
||||
if (willBecomeAndroid) willBecomeAndroid = false
|
||||
if (!isAndroid) return
|
||||
@ -258,8 +367,19 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
if (ply is ServerPlayer) {
|
||||
BecomeHumaneTrigger.trigger(ply)
|
||||
}
|
||||
|
||||
for (feature in featureMap.values) {
|
||||
feature.removeModifiers()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [becomeHumane] plus kills the player
|
||||
*
|
||||
* Does nothing if player is not an Android
|
||||
*
|
||||
* Unsets [willBecomeAndroid] flag if it is true
|
||||
*/
|
||||
fun becomeHumaneAndKill() {
|
||||
if (willBecomeAndroid) willBecomeAndroid = false
|
||||
if (!isAndroid) return
|
||||
@ -268,6 +388,11 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
ply.hurt(MRegistry.DAMAGE_BECOME_HUMANE, ply.maxHealth * 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Refunds* all research and removes all Android features
|
||||
*
|
||||
* Resets [iteration] and death log
|
||||
*/
|
||||
fun obliviate(refund: Boolean = true) {
|
||||
for (instance in research.values) {
|
||||
if (instance.isResearched) {
|
||||
@ -289,13 +414,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
deathLog.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [AndroidResearch] state for specified [AndroidResearchType]
|
||||
*/
|
||||
fun getResearch(type: AndroidResearchType): AndroidResearch {
|
||||
return research.computeIfAbsent(type) {
|
||||
return@computeIfAbsent AndroidResearch(type, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadResearch() {
|
||||
internal fun reloadResearch() {
|
||||
val old = ArrayList<AndroidResearch>(research.size)
|
||||
old.addAll(research.values)
|
||||
research.clear()
|
||||
@ -309,6 +437,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android features stream, use this to get list of all Android features this player has
|
||||
*/
|
||||
val features: Stream<out AndroidFeature> get() = featureMap.values.stream()
|
||||
|
||||
private fun addFeature(feature: AndroidFeature): Boolean {
|
||||
@ -331,6 +462,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or returns specified Android feature to this player
|
||||
*/
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : AndroidFeature> addFeature(feature: AndroidFeatureType<T>): T {
|
||||
val get = featureMap[feature]
|
||||
@ -340,7 +474,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
|
||||
featureMap[feature] = factory
|
||||
|
||||
if (!ply.level.isClientSide) {
|
||||
if (!ply.level.isClientSide && isAndroid) {
|
||||
queuedTicks.add(factory::applyModifiers)
|
||||
}
|
||||
|
||||
@ -356,14 +490,17 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
return factory
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified Android feature from this player
|
||||
*
|
||||
* @return whenever feature was found and removed
|
||||
*/
|
||||
fun removeFeature(feature: AndroidFeatureType<*>): Boolean {
|
||||
val removed = featureMap.remove(feature)
|
||||
|
||||
if (removed != null) {
|
||||
if (!ply.level.isClientSide) {
|
||||
queuedTicks.add {
|
||||
removed.removeModifiers()
|
||||
}
|
||||
if (!ply.level.isClientSide && isAndroid) {
|
||||
queuedTicks.add(removed::removeModifiers)
|
||||
}
|
||||
|
||||
if (ply is ServerPlayer) {
|
||||
@ -374,21 +511,32 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
return removed != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever player has specified [feature]
|
||||
*/
|
||||
fun hasFeature(feature: AndroidFeatureType<*>): Boolean {
|
||||
return featureMap.containsKey(feature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever player has specified [feature] and its [AndroidFeature.level] is equal to or greater than [level]
|
||||
*/
|
||||
fun hasFeatureLevel(feature: AndroidFeatureType<*>, level: Int): Boolean {
|
||||
val get = featureMap[feature] ?: return false
|
||||
return get.level >= level
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw get of Android feature of this player
|
||||
*
|
||||
* @return null if no such feature is present
|
||||
*/
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : AndroidFeature> getFeature(feature: AndroidFeatureType<T>): T? {
|
||||
return featureMap[feature] as T?
|
||||
}
|
||||
|
||||
fun onHurt(event: LivingHurtEvent) {
|
||||
internal fun onHurt(event: LivingHurtEvent) {
|
||||
if (isAndroid) {
|
||||
for (feature in featureMap.values) {
|
||||
feature.onHurt(event)
|
||||
@ -400,16 +548,14 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : AndroidFeature> computeIfAbsent(feature: AndroidFeatureType<T>): T {
|
||||
internal fun <T : AndroidFeature> computeIfAbsent(feature: AndroidFeatureType<T>): T {
|
||||
return getFeature(feature) ?: addFeature(feature)
|
||||
}
|
||||
|
||||
fun <T : AndroidFeature> getFeatureO(feature: AndroidFeatureType<T>): Optional<T> {
|
||||
val get = getFeature(feature)
|
||||
return if (get != null) Optional.of(get) else Optional.empty()
|
||||
}
|
||||
|
||||
fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
|
||||
/**
|
||||
* Lambda style of [getFeature]
|
||||
*/
|
||||
inline fun <T : AndroidFeature> ifFeature(feature: AndroidFeatureType<T>, consumer: (T) -> Unit) {
|
||||
val get = getFeature(feature)
|
||||
|
||||
if (get != null) {
|
||||
@ -543,6 +689,9 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops Android battery as in-world item
|
||||
*/
|
||||
fun dropBattery() {
|
||||
if (androidEnergy.item.isEmpty) return
|
||||
ply.spawnAtLocation(androidEnergy.item)
|
||||
@ -569,7 +718,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
fun tickClient() {
|
||||
private fun tickClient() {
|
||||
queuedTicks.clear()
|
||||
|
||||
if (!ply.isAlive) {
|
||||
@ -585,7 +734,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
tickInventory()
|
||||
}
|
||||
|
||||
fun preTick() {
|
||||
private fun preTick() {
|
||||
if (!ply.isAlive) return
|
||||
|
||||
if (isAndroid) {
|
||||
@ -596,7 +745,7 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
|
||||
}
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
private fun tick() {
|
||||
if (!ply.isAlive) return
|
||||
|
||||
ticksIExist++
|
||||
|
@ -7,6 +7,11 @@ import net.minecraftforge.common.util.INBTSerializable
|
||||
import ru.dbotthepony.mc.otm.core.contains
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* This class represents an `UUID -> Int` map, with each `Int` value being added to [value] property
|
||||
*
|
||||
* This is like Minecraft's Attribute map, except it operate only on whole numbers
|
||||
*/
|
||||
class UUIDIntModifiersMap(private val observer: (Int) -> Unit, private val backingMap: MutableMap<UUID, Int> = HashMap()) : INBTSerializable<ListTag> {
|
||||
var value: Int = 0
|
||||
private set
|
||||
|
Loading…
Reference in New Issue
Block a user