11package gay.`object`.hexdebug.blocks.splicing
22
3+ import at.petrak.hexcasting.api.addldata.ADIotaHolder
4+ import at.petrak.hexcasting.api.block.HexBlockEntity
35import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
6+ import at.petrak.hexcasting.api.casting.iota.Iota
47import at.petrak.hexcasting.api.casting.iota.IotaType
8+ import at.petrak.hexcasting.api.casting.iota.ListIota
59import at.petrak.hexcasting.api.casting.iota.PatternIota
610import at.petrak.hexcasting.api.casting.math.HexPattern
711import at.petrak.hexcasting.api.utils.extractMedia
@@ -29,20 +33,24 @@ import net.minecraft.world.entity.player.Inventory
2933import net.minecraft.world.entity.player.Player
3034import net.minecraft.world.inventory.SimpleContainerData
3135import net.minecraft.world.item.ItemStack
32- import net.minecraft.world.level.block.entity.BlockEntity
3336import net.minecraft.world.level.block.state.BlockState
37+ import kotlin.math.max
3438import kotlin.math.min
3539
36- class SplicingTableBlockEntity (pos : BlockPos , state : BlockState ) : BlockEntity(
37- HexDebugBlockEntities .SPLICING_TABLE .value, pos, state
38- ), ISplicingTable, BaseContainer, MenuProvider {
40+ class SplicingTableBlockEntity (pos : BlockPos , state : BlockState ) :
41+ HexBlockEntity (HexDebugBlockEntities .SPLICING_TABLE .value, pos, state),
42+ ISplicingTable , BaseContainer , MenuProvider , ADIotaHolder
43+ {
3944 override val stacks = BaseContainer .withSize(SplicingTableItemSlot .container_size)
4045
4146 private var listStack by SplicingTableItemSlot .LIST .delegate
4247 private var clipboardStack by SplicingTableItemSlot .CLIPBOARD .delegate
4348 private var mediaStack by SplicingTableItemSlot .MEDIA .delegate
4449 private var staffStack by SplicingTableItemSlot .STAFF .delegate
4550
51+ private val listHolder get() = IXplatAbstractions .INSTANCE .findDataHolder(listStack)
52+ private val clipboardHolder get() = IXplatAbstractions .INSTANCE .findDataHolder(clipboardStack)
53+
4654 private val containerData = SimpleContainerData (SplicingTableDataSlot .size)
4755
4856 private var media by ContainerDataLongDelegate (
@@ -69,8 +77,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
6977 // TODO: save?
7078 private val undoStack = UndoStack ()
7179
72- override fun load (tag : CompoundTag ) {
73- super .load(tag)
80+ override fun loadModData (tag : CompoundTag ) {
7481 ContainerHelper .loadAllItems(tag, stacks)
7582 media = tag.getLong(" media" )
7683 selection = Selection .fromRawIndices(
@@ -80,8 +87,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
8087 viewStartIndex = tag.getInt(" viewStartIndex" )
8188 }
8289
83- override fun saveAdditional (tag : CompoundTag ) {
84- super .saveAdditional(tag)
90+ override fun saveModData (tag : CompoundTag ) {
8591 ContainerHelper .saveAllItems(tag, stacks)
8692 tag.putLong(" media" , media)
8793 tag.putInt(" selectionFrom" , selection?.from ? : - 1 )
@@ -107,8 +113,8 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
107113 undoStack = undoStack,
108114 selection = selection,
109115 viewStartIndex = viewStartIndex,
110- listHolder = IXplatAbstractions . INSTANCE .findDataHolder(listStack) ,
111- clipboardHolder = IXplatAbstractions . INSTANCE .findDataHolder(clipboardStack) ,
116+ listHolder = listHolder ,
117+ clipboardHolder = clipboardHolder ,
112118 )
113119 }
114120
@@ -140,6 +146,8 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
140146 undoStack.clear()
141147 selection = null
142148 viewStartIndex = 0
149+ } else {
150+ (level as ? ServerLevel )?.let { clampView(it, null ) }
143151 }
144152 }
145153
@@ -158,6 +166,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
158166 override fun runAction (action : SplicingTableAction , player : ServerPlayer ? ) {
159167 if (media < mediaCost) return
160168 val data = getData(player) ? : return
169+ clampView(data.level, data)
161170 setupUndoStack(data)
162171 convertAndRun(action.value, data)
163172 }
@@ -176,6 +185,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
176185 val data = getData(player)
177186 ?.let (ReadWriteList ::convertOrNull)
178187 ? : return ResolvedPatternType .ERRORED
188+ clampView(data.level, data)
179189 setupUndoStack(data)
180190 val result = drawPattern(pattern, data)
181191 postRunAction(data)
@@ -214,7 +224,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
214224 }
215225
216226 private fun selectIota (data : ReadList , index : Int , hasShiftDown : Boolean ) {
217- if (! data. isInRange(index)) return
227+ if (! isInRange(data.list, index)) return
218228
219229 val selection = selection
220230 this .selection = if (isOnlyIotaSelected(index)) {
@@ -231,7 +241,7 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
231241 }
232242
233243 private fun selectEdge (data : ReadList , index : Int , hasShiftDown : Boolean ) {
234- if (! isEdgeInRange(data, index)) return
244+ if (! isEdgeInRange(data.list , index)) return
235245
236246 val selection = selection
237247 this .selection = if (isEdgeSelected(index)) {
@@ -255,29 +265,57 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
255265 private fun isEdgeSelected (index : Int ) =
256266 selection?.let { it.start == index && it.end == null } ? : false
257267
258- private fun isEdgeInRange (data : ReadList , index : Int ) =
268+ private fun isInRange (list : List <Iota >? , index : Int ) =
269+ list?.let { index in it.indices } ? : false
270+
271+ private fun isEdgeInRange (list : List <Iota >, index : Int ) =
259272 // cell to the right is in range
260- data. isInRange(index)
273+ isInRange(list, index)
261274 // cell to the left is in range
262- || data. isInRange(index - 1 )
275+ || isInRange(list, index - 1 )
263276 // allow selecting leftmost edge of empty list
264- || (index == 0 && data. list.size == 0 )
277+ || (index == 0 && list.isEmpty() )
265278
266279 private fun postRunAction (data : SplicingTableData ) {
267280 selection = data.selection
268-
269- // if there is no list, or the list is too short to fill the screen, set the start index to 0
270- // otherwise, clamp the start index such that the end index stays in range
271- val lastIndex = data.list?.lastIndex ? : 0
272- val maxStartIndex = lastIndex - VIEW_END_INDEX_OFFSET
273- viewStartIndex = if (maxStartIndex > 0 ) data.viewStartIndex.coerceIn(0 , maxStartIndex) else 0
281+ viewStartIndex = data.viewStartIndex
282+ clampView(data.level, data)
274283
275284 if (data.shouldConsumeMedia) {
276285 media = (media - mediaCost).coerceIn(0 , maxMedia)
277286 refillMedia()
278287 }
279288 }
280289
290+ private fun clampView (level : ServerLevel , data : SplicingTableData ? ) {
291+ val list = if (data != null ) {
292+ data.list
293+ } else {
294+ listHolder?.let { it.readIota(level) as ? ListIota }?.list?.toMutableList()
295+ }
296+
297+ if (list != null ) {
298+ val lastIndex = list.lastIndex
299+ val maxStartIndex = max(0 , lastIndex - VIEW_END_INDEX_OFFSET )
300+
301+ // TODO: gracefully degrade rather than just wiping the whole selection?
302+ when (val selection = selection) {
303+ is Selection .Range -> if (! isInRange(list, selection.end)) {
304+ this .selection = null
305+ }
306+ is Selection .Edge -> if (! isEdgeInRange(list, selection.index)) {
307+ this .selection = null
308+ }
309+ null -> {}
310+ }
311+
312+ viewStartIndex = viewStartIndex.coerceIn(0 , maxStartIndex)
313+ } else {
314+ selection = null
315+ viewStartIndex = 0
316+ }
317+ }
318+
281319 private fun refillMedia () {
282320 val mediaHolder = IXplatAbstractions .INSTANCE .findMediaHolder(mediaStack) ? : return
283321 while (media < maxMedia) {
@@ -294,6 +332,18 @@ class SplicingTableBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
294332 }
295333 }
296334
335+ override fun readIotaTag () = listHolder?.readIotaTag()
336+
337+ override fun writeIota (iota : Iota ? , simulate : Boolean ): Boolean {
338+ val success = listHolder?.writeIota(iota, simulate) ? : false
339+ if (! simulate && success) {
340+ sync()
341+ }
342+ return success
343+ }
344+
345+ override fun writeable () = listHolder?.writeable() ? : false
346+
297347 companion object {
298348 private val config get() = HexDebugConfig .server
299349
0 commit comments