aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/repo/item/SBItemData.kt
blob: 7a6b081bdbd9fbfdda01f7113666d098102e849e (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
package moe.nea.firmament.repo.item

import kotlin.collections.runningFold
import net.minecraft.item.ItemStack
import moe.nea.firmament.repo.RepoManager

class SBItemData {
	private var itemCache: ItemStack? = null

	private val data: MutableMap<SBItemProperty<*>, Any> = mutableMapOf()

	fun <T> getData(prop: SBItemProperty<T>): T? {
		return data[prop] as T?
	}

	fun <Prop : SBItemProperty<T>, T> set(property: Prop, data: T) {
		if (data != null) {
			(this.data as MutableMap<Any, Any>)[property] = data
		} else {
			this.data.remove(property)
		}
		itemCache = null
	}

	private fun <T> enhanceStack(stack: ItemStack, property: SBItemProperty.State<T>): ItemStack {
		val data = getData(property)
		return property.applyToStack(stack, this, data)
	}

	private fun createStack(): ItemStack {
		return SBItemProperty.allStates.fold(ItemStack.EMPTY) { stack, prop ->
			enhanceStack(stack, prop)
		}
	}

	fun debugStackCreation(): List<PartialStack<*>> {
		fun <T> combinedEnhanceStack(previous: PartialStack<*>, mod: SBItemProperty.State<T>): PartialStack<T> {
			val nextStack = enhanceStack(previous.stack.copy(), mod)
			return PartialStack(mod, getData(mod), nextStack)
		}
		return SBItemProperty.allStates
			.runningFold(
				PartialStack<Nothing>(null, null, ItemStack.EMPTY)) { stack: PartialStack<*>, prop ->
				combinedEnhanceStack(stack, prop)
			}
	}

	/**
	 * Creates an [ItemStack] based on the current properties. The returned item stack must not be modified by the
	 * caller.
	 */
	fun toImmutableStack(): ItemStack {
		var cached = itemCache
		if (cached == null) {
			cached = createStack()
			itemCache = cached
		}
		return cached
	}

	data class PartialStack<T>(
		val lastAppliedModifier: SBItemProperty<T>?,
		val data: T?,
		val stack: ItemStack,
	)

	companion object {
		/**
		 * Create an [SBItemData] from only the given characteristica. Any unspecified characteristica will be non-existent.
		 * If you want to compute all other properties based on the given properties, use [roundtrip].
		 */
		fun fromCharacteristica(
			vararg char: SBItemProperty.BoundState<*>
		): SBItemData {
			val store = SBItemData()
			char.forEach {
				it.applyTo(store)
			}
			return store
		}

		fun fromStack(itemStack: ItemStack): SBItemData {
			val store = SBItemData()
			store.loadFrom(itemStack)
			return store
		}
	}

	/**
	 * Creates a new [SBItemData] from the item stack this [SBItemData] produces. This will initialize all properties.
	 */
	fun roundtrip(): SBItemData {
		return fromStack(toImmutableStack())
	}

	/**
	 * Creates a new [SBItemData] with cheap inferences completed only by using data available in [io.github.moulberry.repo.data.NEUItem]. This is a cheaper version of [roundtrip], that does not create any [ItemStack]s, and preserves all properties already provided. Check if the property you need overrides [SBItemProperty.fromNeuItem].
	 */
	fun cheapInfer(): SBItemData {
		val neuItem = getData(SBItemId)?.let { RepoManager.getNEUItem(it) } ?: return this
		val store = SBItemData()
		SBItemProperty.allProperties.forEach {
			it.fromNeuItem(neuItem, this)
		}
		store.data.putAll(this.data)
		return store
	}

	private fun loadFrom(stack: ItemStack) {
		SBItemProperty.allProperties.forEach {
			loadModifier(stack, it)
		}
	}

	private fun <T> loadModifier(
		stack: ItemStack,
		modifier: SBItemProperty<T>
	) {
		val data = modifier.fromStack(stack, this) ?: return
		set(modifier, data)
	}
}