container: slot types, sections

This commit is contained in:
Bixilon 2022-02-25 13:05:48 +01:00
parent c8068a6867
commit d69fae0627
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
19 changed files with 352 additions and 112 deletions

View File

@ -45,12 +45,6 @@
},
"end": {
"$ref": "#/definitions/vec2i"
},
"section": {
"type": "integer"
},
"type": {
"enum": ["default", "main_hand", "off_hand", "feet", "legs", "chest", "head", "read_only"]
}
},
"required": [

View File

@ -21,8 +21,11 @@ import de.bixilon.kutil.watcher.DataWatcher.Companion.observe
import de.bixilon.kutil.watcher.DataWatcher.Companion.watched
import de.bixilon.kutil.watcher.map.MapDataWatcher.Companion.watchedMap
import de.bixilon.minosoft.data.inventory.click.ContainerAction
import de.bixilon.minosoft.data.inventory.click.SlotSwapContainerAction
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.inventory.stack.property.HolderProperty
import de.bixilon.minosoft.data.registries.other.containers.slots.DefaultSlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.SlotType
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.c2s.play.container.CloseContainerC2SP
@ -75,6 +78,10 @@ open class Container(
lock.unlock()
}
open fun getSlotType(slotId: Int): SlotType? = DefaultSlotType
open fun getSlotSwap(slot: SlotSwapContainerAction.SwapTargets): Int? = null
open fun getSection(slotId: Int): Int? = null
operator fun get(slotId: Int): ItemStack? {
try {
lock.acquire()

View File

@ -16,8 +16,16 @@ package de.bixilon.minosoft.data.registries.other.containers
import de.bixilon.kutil.collections.CollectionUtil.lockMapOf
import de.bixilon.kutil.collections.map.LockMap
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.click.SlotSwapContainerAction
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.data.registries.other.containers.slots.DefaultSlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.RemoveOnlySlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.SlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.equipment.ChestSlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.equipment.FeetSlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.equipment.HeadSlotType
import de.bixilon.minosoft.data.registries.other.containers.slots.equipment.LegsSlotType
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@ -62,6 +70,45 @@ class PlayerInventory(connection: PlayConnection) : Container(
super.set(*realSlots.toTypedArray())
}
override fun getSlotType(slotId: Int): SlotType? {
return when (slotId) {
0 -> RemoveOnlySlotType // crafting result
in 1..4 -> DefaultSlotType // crafting
ARMOR_OFFSET + 0 -> HeadSlotType
ARMOR_OFFSET + 1 -> ChestSlotType
ARMOR_OFFSET + 2 -> LegsSlotType
ARMOR_OFFSET + 3 -> FeetSlotType
in ARMOR_OFFSET + 3..HOTBAR_OFFSET + HOTBAR_SLOTS + 1 -> DefaultSlotType // all slots, including offhand
else -> null
}
}
override fun getSlotSwap(slot: SlotSwapContainerAction.SwapTargets): Int {
return when (slot) {
SlotSwapContainerAction.SwapTargets.HOTBAR_1 -> HOTBAR_OFFSET + 0
SlotSwapContainerAction.SwapTargets.HOTBAR_2 -> HOTBAR_OFFSET + 1
SlotSwapContainerAction.SwapTargets.HOTBAR_3 -> HOTBAR_OFFSET + 2
SlotSwapContainerAction.SwapTargets.HOTBAR_4 -> HOTBAR_OFFSET + 3
SlotSwapContainerAction.SwapTargets.HOTBAR_5 -> HOTBAR_OFFSET + 4
SlotSwapContainerAction.SwapTargets.HOTBAR_6 -> HOTBAR_OFFSET + 5
SlotSwapContainerAction.SwapTargets.HOTBAR_7 -> HOTBAR_OFFSET + 6
SlotSwapContainerAction.SwapTargets.HOTBAR_8 -> HOTBAR_OFFSET + 7
SlotSwapContainerAction.SwapTargets.HOTBAR_9 -> HOTBAR_OFFSET + 8
SlotSwapContainerAction.SwapTargets.OFFHAND -> 45
}
}
override fun getSection(slotId: Int): Int? {
return when (slotId) {
in 0..4 -> null // crafting
in ARMOR_OFFSET..ARMOR_OFFSET + 4 -> 0 // armor
in ARMOR_OFFSET + 5 until HOTBAR_OFFSET -> 1 // inventory
in HOTBAR_OFFSET..HOTBAR_OFFSET + HOTBAR_SLOTS -> 2 // hotbar
else -> null // offhand, else
}
}
val InventorySlots.EquipmentSlots.slot: Int
get() {
return when (this) {

View File

@ -0,0 +1,16 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots
object DefaultSlotType : SlotType

View File

@ -0,0 +1,24 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.other.containers.Container
object ReadOnlySlotType : SlotType {
override fun canRemove(container: Container, slot: Int, stack: ItemStack): Boolean = false
override fun canModify(container: Container, slot: Int, stack: ItemStack): Boolean = false
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean = false
}

View File

@ -0,0 +1,24 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.other.containers.Container
object RemoveOnlySlotType : SlotType {
override fun canRemove(container: Container, slot: Int, stack: ItemStack): Boolean = true
override fun canModify(container: Container, slot: Int, stack: ItemStack): Boolean = true
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean = true
}

View File

@ -0,0 +1,24 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.other.containers.Container
interface SlotType {
fun canRemove(container: Container, slot: Int, stack: ItemStack) = true
fun canModify(container: Container, slot: Int, stack: ItemStack) = true
fun canPut(container: Container, slot: Int, stack: ItemStack) = true
}

View File

@ -0,0 +1,30 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.items.armor.ArmorItem
import de.bixilon.minosoft.data.registries.other.containers.Container
object ChestSlotType : EquipmentSlotType {
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean {
val item = stack.item.item
if (item !is ArmorItem) {
return false
}
return item.equipmentSlot == InventorySlots.EquipmentSlots.CHEST
}
}

View File

@ -0,0 +1,16 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
interface EquipmentSlotType : SingleItemSlotType

View File

@ -0,0 +1,30 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.items.armor.ArmorItem
import de.bixilon.minosoft.data.registries.other.containers.Container
object FeetSlotType : EquipmentSlotType {
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean {
val item = stack.item.item
if (item !is ArmorItem) {
return false
}
return item.equipmentSlot == InventorySlots.EquipmentSlots.FEET
}
}

View File

@ -0,0 +1,31 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.items.armor.ArmorItem
import de.bixilon.minosoft.data.registries.other.containers.Container
object HeadSlotType : EquipmentSlotType {
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean {
val item = stack.item.item
// ToDo: Carved pumpkin
if (item !is ArmorItem) {
return false
}
return item.equipmentSlot == InventorySlots.EquipmentSlots.HEAD
}
}

View File

@ -0,0 +1,30 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.stack.ItemStack
import de.bixilon.minosoft.data.registries.items.armor.ArmorItem
import de.bixilon.minosoft.data.registries.other.containers.Container
object LegsSlotType : EquipmentSlotType {
override fun canPut(container: Container, slot: Int, stack: ItemStack): Boolean {
val item = stack.item.item
if (item !is ArmorItem) {
return false
}
return item.equipmentSlot == InventorySlots.EquipmentSlots.LEGS
}
}

View File

@ -0,0 +1,18 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.other.containers.slots.equipment
import de.bixilon.minosoft.data.registries.other.containers.slots.SlotType
interface SingleItemSlotType : SlotType

View File

@ -22,7 +22,7 @@ class AtlasElement(
override val texture: AbstractTexture,
val start: Vec2i,
val end: Vec2i,
val slots: Int2ObjectOpenHashMap<Vec2iBinding>, // ToDo: Use an array?
val slots: Int2ObjectOpenHashMap<AtlasSlot>,
) : TextureLike {
override val size: Vec2i = end - start
override lateinit var uvStart: Vec2

View File

@ -60,17 +60,18 @@ class AtlasManager(private val renderWindow: RenderWindow) {
val texture = renderWindow.textureManager.staticTextures.createTexture(versionData["texture"].toResourceLocation(), mipmaps = false)
val start = versionData["start"].toVec2i()
val end = versionData["end"].toVec2i()
val slots: Int2ObjectOpenHashMap<Vec2iBinding> = Int2ObjectOpenHashMap()
val slots: Int2ObjectOpenHashMap<AtlasSlot> = Int2ObjectOpenHashMap()
versionData["slots"].toJsonObject()?.let {
for ((slotId, slotData) in it) {
val slot = slotData.asJsonObject()
slots[slotId.toInt()] = Vec2iBinding(
slots[slotId.toInt()] = AtlasSlot(
start = slot["start"].toVec2i(),
end = slot["end"].toVec2i(),
)
}
}
// ToDo: special
val atlasElement = AtlasElement(
texture = texture,

View File

@ -15,7 +15,7 @@ package de.bixilon.minosoft.gui.rendering.gui.atlas
import glm_.vec2.Vec2i
data class Vec2iBinding(
data class AtlasSlot(
val start: Vec2i,
val end: Vec2i,
) {

View File

@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.elements.items
import de.bixilon.kutil.watcher.map.MapDataWatcher.Companion.observeMap
import de.bixilon.minosoft.data.registries.other.containers.Container
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.atlas.Vec2iBinding
import de.bixilon.minosoft.gui.rendering.gui.atlas.AtlasSlot
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.gui.AbstractLayout
import de.bixilon.minosoft.gui.rendering.gui.gui.dragged.elements.item.FloatingItem
@ -32,7 +32,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
class ContainerItemsElement(
guiRenderer: GUIRenderer,
val container: Container,
val slots: Int2ObjectOpenHashMap<Vec2iBinding>, // ToDo: Use an array?
val slots: Int2ObjectOpenHashMap<AtlasSlot>, // ToDo: Use an array?
) : Element(guiRenderer), AbstractLayout<ItemElement> {
private val itemElements: Int2ObjectOpenHashMap<ItemElementData> = Int2ObjectOpenHashMap()
private var floatingItem: FloatingItem? = null

View File

@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.container
import de.bixilon.minosoft.data.registries.other.containers.Container
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.atlas.AtlasElement
import de.bixilon.minosoft.gui.rendering.gui.atlas.Vec2iBinding
import de.bixilon.minosoft.gui.rendering.gui.atlas.AtlasSlot
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.elements.items.ContainerItemsElement
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.AtlasImageElement
@ -33,7 +33,7 @@ abstract class ContainerScreen(
guiRenderer: GUIRenderer,
val container: Container,
background: AtlasElement,
items: Int2ObjectOpenHashMap<Vec2iBinding> = background.slots,
items: Int2ObjectOpenHashMap<AtlasSlot> = background.slots,
) : Screen(guiRenderer), AbstractLayout<Element> {
private val containerBackground = AtlasImageElement(guiRenderer, background)
protected val containerElement = ContainerItemsElement(guiRenderer, container, items).apply { parent = this@ContainerScreen }

View File

@ -715,239 +715,187 @@
"slots": {
"0": {
"start": [154, 28],
"end": [171, 45],
"type": "read_only",
"section": -1
"end": [171, 45]
},
"1": {
"start": [98, 18],
"end": [115, 35],
"section": -1
"end": [115, 35]
},
"2": {
"start": [116, 18],
"end": [133, 35],
"section": -1
"end": [133, 35]
},
"3": {
"start": [98, 36],
"end": [115, 53],
"section": -1
"end": [115, 53]
},
"4": {
"start": [116, 36],
"end": [133, 53],
"section": -1
"end": [133, 53]
},
"5": {
"start": [8, 8],
"end": [25, 25],
"section": 0,
"type": "head"
"end": [25, 25]
},
"6": {
"start": [8, 26],
"end": [25, 43],
"section": 0,
"type": "chest"
"end": [25, 43]
},
"7": {
"start": [8, 44],
"end": [25, 61],
"section": 0,
"type": "legs"
"end": [25, 61]
},
"8": {
"start": [8, 62],
"end": [25, 79],
"section": 0,
"type": "feet"
"end": [25, 79]
},
"9": {
"start": [8, 84],
"end": [25, 101],
"section": 1
"end": [25, 101]
},
"10": {
"start": [26, 84],
"end": [43, 101],
"section": 1
"end": [43, 101]
},
"11": {
"start": [44, 84],
"end": [61, 101],
"section": 1
"end": [61, 101]
},
"12": {
"start": [62, 84],
"end": [79, 101],
"section": 1
"end": [79, 101]
},
"13": {
"start": [80, 84],
"end": [97, 101],
"section": 1
"end": [97, 101]
},
"14": {
"start": [98, 84],
"end": [115, 101],
"section": 1
"end": [115, 101]
},
"15": {
"start": [116, 84],
"end": [133, 101],
"section": 1
"end": [133, 101]
},
"16": {
"start": [134, 84],
"end": [151, 101],
"section": 1
"end": [151, 101]
},
"17": {
"start": [152, 84],
"end": [169, 101],
"section": 1
"end": [169, 101]
},
"18": {
"start": [8, 102],
"end": [25, 119],
"section": 1
"end": [25, 119]
},
"19": {
"start": [26, 102],
"end": [43, 119],
"section": 1
"end": [43, 119]
},
"20": {
"start": [44, 102],
"end": [61, 119],
"section": 1
"end": [61, 119]
},
"21": {
"start": [62, 102],
"end": [79, 119],
"section": 1
"end": [79, 119]
},
"22": {
"start": [80, 102],
"end": [97, 119],
"section": 1
"end": [97, 119]
},
"23": {
"start": [98, 102],
"end": [115, 119],
"section": 1
"end": [115, 119]
},
"24": {
"start": [116, 102],
"end": [133, 119],
"section": 1
"end": [133, 119]
},
"25": {
"start": [134, 102],
"end": [151, 119],
"section": 1
"end": [151, 119]
},
"26": {
"start": [152, 102],
"end": [169, 119],
"section": 1
"end": [169, 119]
},
"27": {
"start": [8, 120],
"end": [25, 137],
"section": 1
"end": [25, 137]
},
"28": {
"start": [26, 120],
"end": [43, 137],
"section": 1
"end": [43, 137]
},
"29": {
"start": [44, 120],
"end": [61, 137],
"section": 1
"end": [61, 137]
},
"30": {
"start": [62, 120],
"end": [79, 137],
"section": 1
"end": [79, 137]
},
"31": {
"start": [80, 120],
"end": [97, 137],
"section": 1
"end": [97, 137]
},
"32": {
"start": [98, 120],
"end": [115, 137],
"section": 1
"end": [115, 137]
},
"33": {
"start": [116, 120],
"end": [133, 137],
"section": 1
"end": [133, 137]
},
"34": {
"start": [134, 120],
"end": [151, 137],
"section": 1
"end": [151, 137]
},
"35": {
"start": [152, 120],
"end": [169, 137],
"section": 1
"end": [169, 137]
},
"36": {
"start": [8, 142],
"end": [25, 159],
"section": 2
"end": [25, 159]
},
"37": {
"start": [26, 142],
"end": [43, 159],
"section": 2
"end": [43, 159]
},
"38": {
"start": [44, 142],
"end": [61, 159],
"section": 2
"end": [61, 159]
},
"39": {
"start": [62, 142],
"end": [79, 159],
"section": 2
"end": [79, 159]
},
"40": {
"start": [80, 142],
"end": [97, 159],
"section": 2
"end": [97, 159]
},
"41": {
"start": [98, 142],
"end": [115, 159],
"section": 2
"end": [115, 159]
},
"42": {
"start": [116, 142],
"end": [133, 159],
"section": 2
"end": [133, 159]
},
"43": {
"start": [134, 142],
"end": [151, 159],
"section": 2
"end": [151, 159]
},
"44": {
"start": [152, 142],
"end": [169, 159],
"section": 2
"end": [169, 159]
},
"45": {
"start": [77, 62],
"end": [94, 79],
"type": "off_hand",
"section": -1
"end": [94, 79]
}
},
"areas": {