diff --git a/assets/Data/entity/creatures/human.json b/assets/Data/entity/creatures/human.json index 6cbaa57e..e6568662 100644 --- a/assets/Data/entity/creatures/human.json +++ b/assets/Data/entity/creatures/human.json @@ -267,7 +267,8 @@ "nameFirstPerson" : "Idle", "priorityCategory" : "MODIFIER_HIGH", "boneGroups" : ["handRight"] - } + }, + "isToolbarSlot": true }, { "equipPointId" : "handsCombined", @@ -289,6 +290,7 @@ "priorityCategory" : "MODIFIER_HIGH", "boneGroups" : ["armLeft", "armRight", "handLeft", "handRight"] }, + "isToolbarSlot": true, "isCombinedPoint": true, "subPoints" : ["handLeft","handRight"] }, @@ -317,6 +319,10 @@ ] } ], + "toolbarData" : { + "primarySlot" : "handRight", + "combinedSlot" : "handsCombined" + }, "blockSystem" : { "variants": [ { diff --git a/assets/Data/entity/creatures/skeleton.json b/assets/Data/entity/creatures/skeleton.json index 19a47a1b..5d59e106 100644 --- a/assets/Data/entity/creatures/skeleton.json +++ b/assets/Data/entity/creatures/skeleton.json @@ -169,7 +169,8 @@ "nameFirstPerson" : "Idle", "priorityCategory" : "MODIFIER_HIGH", "boneGroups" : ["handRight"] - } + }, + "isToolbarSlot": true }, { "equipPointId" : "handsCombined", @@ -191,6 +192,7 @@ "priorityCategory" : "MODIFIER_HIGH", "boneGroups" : ["armLeft", "armRight", "handLeft", "handRight"] }, + "isToolbarSlot": true, "isCombinedPoint": true, "subPoints" : ["handLeft","handRight"] }, @@ -219,6 +221,10 @@ ] } ], + "toolbarData" : { + "primarySlot" : "handRight", + "combinedSlot" : "handsCombined" + }, "blockSystem" : { "variants": [ { diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index fe5759b0..57c06fdf 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -845,6 +845,13 @@ Partially fix idle animations for human + skeleton Directional lighting color control Skysphere affected by directional lighting +(09/25/2024) +(09/26/2024) +Work on toolbar refactor + +(09/27/2024) +Toolbar state mostly working + # TODO diff --git a/net/inventory.json b/net/inventory.json index c02d9021..dcb74ac5 100644 --- a/net/inventory.json +++ b/net/inventory.json @@ -25,6 +25,10 @@ "name" : "containerType", "type" : "FIXED_INT" }, + { + "name" : "toolbarId", + "type" : "FIXED_INT" + }, { "name" : "itemActionCode", "type" : "FIXED_INT" @@ -37,6 +41,7 @@ "messageTypes" : [ { "messageName" : "addItemToInventory", + "description" : "Requests that the server add the item to the client entity's appropriate inventory", "data" : [ "entityId", "itemTemplate" @@ -70,6 +75,7 @@ "description" : "Instructs the client to equip an item to an entity", "data" : [ "equipperId", + "containerType", "equipPointId", "entityId", "itemTemplate" @@ -80,6 +86,7 @@ "description" : "Instructs the client to unequip an item", "data" : [ "equipperId", + "containerType", "equipPointId" ] }, @@ -90,6 +97,21 @@ "equipPointId" ] }, + { + "messageName" : "clientRequestAddToolbar", + "description" : "Requests that the server add the item to the client entity's toolbar", + "data" : [ + "entityId", + "toolbarId" + ] + }, + { + "messageName" : "clientRequestAddNatural", + "description" : "Requests that the server add the item to the client entity's natural inventory", + "data" : [ + "entityId" + ] + }, { "messageName" : "clientRequestPerformItemAction", "description" : "Requests that the server have the entity perform its equipped item's action for the given equip point", diff --git a/src/main/java/electrosphere/client/item/ItemActions.java b/src/main/java/electrosphere/client/item/ItemActions.java index 1c44392c..a2fc33b7 100644 --- a/src/main/java/electrosphere/client/item/ItemActions.java +++ b/src/main/java/electrosphere/client/item/ItemActions.java @@ -7,6 +7,7 @@ import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.entity.state.equip.ClientToolbarState; import electrosphere.game.data.item.type.Item; import electrosphere.net.parser.net.message.InventoryMessage; @@ -33,10 +34,10 @@ public class ItemActions { //tell the server we want the secondary hand item to START doing something Globals.clientConnection.queueOutgoingMessage(InventoryMessage.constructclientRequestPerformItemActionMessage("handRight", ITEM_ACTION_CODE_SECONDARY, ITEM_ACTION_CODE_STATE_ON)); //TODO: do any immediate client side calculations here (ie start playing an animation until we get response from server) - ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(Globals.playerEntity); - Entity rightHandItem = clientEquipState.getEquippedItemAtPoint("handRight"); - if(rightHandItem != null && Globals.gameConfigCurrent.getItemMap().getItem(rightHandItem) != null){ - Item data = Globals.gameConfigCurrent.getItemMap().getItem(rightHandItem); + ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(Globals.playerEntity); + Entity primaryEntity = clientToolbarState.getCurrentPrimaryItem(); + if(primaryEntity != null && Globals.gameConfigCurrent.getItemMap().getItem(primaryEntity) != null){ + Item data = Globals.gameConfigCurrent.getItemMap().getItem(primaryEntity); if(data.getClientSidePrimary() != null){ switch(data.getClientSidePrimary()){ case "OPEN_VOXEL": { diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java index 3babcfbc..63070fcc 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java @@ -13,8 +13,8 @@ import org.joml.Vector3i; import electrosphere.auth.AuthenticationManager; import electrosphere.engine.Globals; import electrosphere.engine.threads.LabeledThread.ThreadLabel; -import electrosphere.entity.types.creature.CreatureEquipData.EquippedItem; import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.entity.types.creature.CreatureToolbarData.ToolbarItem; import electrosphere.game.data.creature.type.CreatureData; import electrosphere.game.data.creature.type.visualattribute.VisualAttribute; import electrosphere.game.server.world.MacroData; @@ -174,7 +174,7 @@ public class LoadingUtils { template.putAttributeValue(attribute.getAttributeId(), attribute.getVariants().get(0).getId()); } } - template.getCreatureEquipData().setSlotItem("handRight",new EquippedItem(71, "terrainTool")); + template.getCreatureToolbarData().setSlotItem("0", new ToolbarItem(71, "terrainTool")); //set player character template serverPlayerConnection.setCreatureTemplate(template); Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); diff --git a/src/main/java/electrosphere/entity/ClientEntityUtils.java b/src/main/java/electrosphere/entity/ClientEntityUtils.java index bf5e5e0a..f371a12e 100644 --- a/src/main/java/electrosphere/entity/ClientEntityUtils.java +++ b/src/main/java/electrosphere/entity/ClientEntityUtils.java @@ -32,20 +32,22 @@ public class ClientEntityUtils { */ public static void destroyEntity(Entity entity){ - // - //destroy the child entities, too - if(AttachUtils.hasChildren(entity)){ - List children = AttachUtils.getChildrenList(entity); - for(Entity child : children){ - ClientEntityUtils.destroyEntity(child); + if(entity != null){ + // + //destroy the child entities, too + if(AttachUtils.hasChildren(entity)){ + List children = AttachUtils.getChildrenList(entity); + for(Entity child : children){ + ClientEntityUtils.destroyEntity(child); + } } + + //check for client-specific stuff + Globals.renderingEngine.getLightManager().destroyPointLight(entity); + + //deregister all behavior trees + EntityUtils.cleanUpEntity(entity); } - - //check for client-specific stuff - Globals.renderingEngine.getLightManager().destroyPointLight(entity); - - //deregister all behavior trees - EntityUtils.cleanUpEntity(entity); } diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index cc721920..d6ae0dfb 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -259,6 +259,8 @@ public class EntityDataStrings { public static final String TREE_CLIENTEQUIPSTATE = "treeClientEquipState"; public static final String EQUIP_INVENTORY = "equipInventory"; public static final String TREE_SERVEREQUIPSTATE = "treeServerEquipState"; + public static final String TREE_CLIENTTOOLBARSTATE = "treeClientToolbarState"; + public static final String TREE_SERVERTOOLBARSTATE = "treeServerToolbarState"; /* Client-only components diff --git a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java index 359682b6..8d5603db 100644 --- a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java +++ b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java @@ -85,11 +85,10 @@ public class ClientEquipState implements BehaviorTree { public void commandAttemptEquip(Entity toEquip, EquipPoint point){ boolean hasEquipped = hasEquippedAtPoint(point.getEquipPointId()); boolean targetIsItem = ItemUtils.isItem(toEquip); - boolean targetIsAttached = AttachUtils.isAttached(toEquip); String equipItemClass = ItemUtils.getEquipClass(toEquip); List pointEquipClassList = point.getEquipClassWhitelist(); boolean itemIsInPointWhitelist = pointEquipClassList.contains(equipItemClass); - if(!hasEquipped && targetIsItem && !targetIsAttached && itemIsInPointWhitelist){ + if(!hasEquipped && targetIsItem && itemIsInPointWhitelist){ //send packet to server requesting to equip String pointName = point.getEquipPointId(); int serverSideID = Globals.clientSceneWrapper.mapClientToServerId(toEquip.getId()); diff --git a/src/main/java/electrosphere/entity/state/equip/ClientToolbarState.java b/src/main/java/electrosphere/entity/state/equip/ClientToolbarState.java new file mode 100644 index 00000000..0070abe7 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/equip/ClientToolbarState.java @@ -0,0 +1,323 @@ +package electrosphere.entity.state.equip; + + +import electrosphere.entity.btree.BehaviorTree; +import electrosphere.entity.state.attach.AttachUtils; +import electrosphere.entity.state.client.firstPerson.FirstPersonTree; +import electrosphere.entity.state.gravity.GravityUtils; +import electrosphere.entity.state.inventory.InventoryUtils; +import electrosphere.entity.state.inventory.RelationalInventoryState; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.entity.types.item.ItemUtils; +import electrosphere.game.data.common.treedata.TreeDataAnimation; +import electrosphere.game.data.creature.type.equip.EquipPoint; +import electrosphere.game.data.creature.type.equip.ToolbarData; +import electrosphere.game.data.item.type.EquipWhitelist; +import electrosphere.game.data.item.type.Item; + +import java.util.List; + +import electrosphere.collision.PhysicsEntityUtils; +import electrosphere.engine.Globals; +import electrosphere.entity.ClientEntityUtils; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityCreationUtils; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityTags; +import electrosphere.entity.EntityUtils; +import electrosphere.net.synchronization.enums.BehaviorTreeIdEnums; +import electrosphere.renderer.actor.Actor; +import electrosphere.renderer.actor.ActorMeshMask; +import electrosphere.net.parser.net.message.InventoryMessage; +import electrosphere.net.parser.net.message.NetworkMessage; +import electrosphere.net.synchronization.annotation.SyncedField; +import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; + +/** + * The state of the toolbar on the client's side + */ +@SynchronizedBehaviorTree(name = "clientToolbarState", isServer = false, correspondingTree="serverToolbarState") +public class ClientToolbarState implements BehaviorTree { + + /** + * The selected toolbar slot + */ + @SyncedField + int selectedSlot; + + /** + * The parent entity + */ + Entity parent; + + /** + * The toolbar data + */ + ToolbarData toolbarData; + + /** + * The equipped entity + */ + Entity equippedEntity = null; + + /** + * Attempts to add an item to the toolbar + * @param item The item + */ + public void attemptAddToToolbar(Entity item, int toolbarSlot){ + if(item != null){ + NetworkMessage requestUnequipMessage = InventoryMessage.constructclientRequestAddToolbarMessage(Globals.clientSceneWrapper.mapClientToServerId(item.getId()), toolbarSlot); + Globals.clientConnection.queueOutgoingMessage(requestUnequipMessage); + } + } + + /** + * Performs the actual logic to term meshes on/off when equpping an item + * @param toEquip The entity to equip + */ + public void attemptEquip(Entity toEquip){ + RelationalInventoryState equipInventoryState = InventoryUtils.getEquipInventory(parent); + boolean targetHasWhitelist = ItemUtils.hasEquipList(toEquip); + String equipItemClass = ItemUtils.getEquipClass(toEquip); + EquipPoint targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getPrimarySlot()); + if(targetPoint.getEquipClassWhitelist() != null && !targetPoint.getEquipClassWhitelist().contains(equipItemClass)){ + targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getCombinedSlot()); + } + equippedEntity = toEquip; + + // + //visual transforms + if(targetHasWhitelist){ + //depends on the type of creature, must be replacing a mesh + String parentCreatureId = CreatureUtils.getType(parent); + List whitelist = ItemUtils.getEquipWhitelist(toEquip); + for(EquipWhitelist whitelistItem : whitelist){ + if(whitelistItem.getCreatureId().equals(parentCreatureId)){ + //put in map + String modelName = whitelistItem.getModel(); + Globals.assetManager.addModelPathToQueue(modelName); + Actor parentActor = EntityUtils.getActor(parent); + //queue meshes from display model to parent actor + ActorMeshMask meshMask = parentActor.getMeshMask(); + for(String toBlock : whitelistItem.getMeshMaskList()){ + meshMask.blockMesh(modelName, toBlock); + } + for(String toDraw : whitelistItem.getMeshList()){ + meshMask.queueMesh(modelName, toDraw); + } + //attach to parent bone + if(parent != Globals.firstPersonEntity || Globals.controlHandler.cameraIsThirdPerson()){ + AttachUtils.clientAttachEntityToEntityAtBone( + parent, + toEquip, + targetPoint.getBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorThirdPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationThirdPerson()) + ); + } else { + AttachUtils.clientAttachEntityToEntityAtBone( + Globals.firstPersonEntity, + toEquip, + targetPoint.getFirstPersonBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorFirstPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationFirstPerson()) + ); + } + //make uncollidable + if(PhysicsEntityUtils.containsDBody(toEquip) && toEquip.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ + Globals.clientSceneWrapper.getCollisionEngine().destroyPhysics(toEquip); + } + //make untargetable + Globals.clientSceneWrapper.getScene().removeEntityFromTag(toEquip, EntityTags.TARGETABLE); + break; + } + } + } else { + //does not depend on the type of creature, must be attaching to a bone + //make sure it's visible + if(EntityUtils.getActor(toEquip) == null){ + Item itemData = Globals.gameConfigCurrent.getItemMap().getItem(ItemUtils.getType(toEquip)); + EntityCreationUtils.makeEntityDrawable(toEquip, itemData.getGraphicsTemplate().getModel().getPath()); + if(itemData.getIdleAnim() != null){ + toEquip.putData(EntityDataStrings.ANIM_IDLE,itemData.getIdleAnim()); + } + } + //actually equip + if(parent != Globals.firstPersonEntity || Globals.controlHandler.cameraIsThirdPerson()){ + AttachUtils.clientAttachEntityToEntityAtBone( + parent, + toEquip, + targetPoint.getBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorThirdPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationThirdPerson()) + ); + } else { + AttachUtils.clientAttachEntityToEntityAtBone( + Globals.firstPersonEntity, + toEquip, + targetPoint.getFirstPersonBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorFirstPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationFirstPerson()) + ); + } + if(PhysicsEntityUtils.containsDBody(toEquip) && toEquip.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ + Globals.clientSceneWrapper.getCollisionEngine().destroyPhysics(toEquip); + } + Globals.clientSceneWrapper.getScene().removeEntityFromTag(toEquip, EntityTags.TARGETABLE); + GravityUtils.clientAttemptDeactivateGravity(toEquip); + } + } + + /** + * Performs the actual logic to turn meshes on/off when unequipping an item + * @param pointId The equipment point to unequip + */ + public void unequip(String pointId){ + if(this.equippedEntity != null){ + boolean targetHasWhitelist = ItemUtils.hasEquipList(this.equippedEntity); + RelationalInventoryState equipInventoryState = InventoryUtils.getEquipInventory(parent); + String equipItemClass = ItemUtils.getEquipClass(this.equippedEntity); + + EquipPoint targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getPrimarySlot()); + if(targetPoint.getEquipClassWhitelist() != null && !targetPoint.getEquipClassWhitelist().contains(equipItemClass)){ + targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getCombinedSlot()); + } + + // + //visual transforms + if(targetHasWhitelist){ + //depends on the type of creature, must be replacing meshes + String parentCreatureId = CreatureUtils.getType(parent); + List whitelist = ItemUtils.getEquipWhitelist(this.equippedEntity); + for(EquipWhitelist whitelistItem : whitelist){ + if(whitelistItem.getCreatureId().equals(parentCreatureId)){ + //put in map + Actor parentActor = EntityUtils.getActor(parent); + //queue meshes from display model to parent actor + ActorMeshMask meshMask = parentActor.getMeshMask(); + for(String toUnblock : whitelistItem.getMeshMaskList()){ + meshMask.unblockMesh(toUnblock); + } + for(String toDraw : whitelistItem.getMeshList()){ + meshMask.removeAdditionalMesh(toDraw); + } + break; + } + } + } else { + //does not depend on the type of creature + AttachUtils.clientDetatchEntityFromEntityAtBone(parent, this.equippedEntity); + ClientEntityUtils.destroyEntity(this.equippedEntity); + } + + //interrupt animation + if(targetPoint != null){ + Actor thirdPersonActor = EntityUtils.getActor(parent); + if(targetPoint.getEquippedAnimation() != null){ + TreeDataAnimation animation = targetPoint.getEquippedAnimation(); + //play third person + if(thirdPersonActor.isPlayingAnimation() && thirdPersonActor.isPlayingAnimation(animation)){ + if(animation != null){ + thirdPersonActor.interruptAnimation(animation,true); + } + thirdPersonActor.incrementAnimationTime(0.0001); + } + + //play first person + FirstPersonTree.conditionallyInterruptAnimation(parent, animation); + } + } + } + } + + /** + * Gets the current primary item if it exists, null otherwise + * @return The item + */ + public Entity getCurrentPrimaryItem(){ + return this.equippedEntity; + } + + + /** + *

(initially) Automatically generated

+ *

+ * Attaches this tree to the entity. + *

+ * @param entity The entity to attach to + * @param tree The behavior tree to attach + * @param params Optional parameters that will be provided to the constructor + */ + public static ClientToolbarState attachTree(Entity parent, Object ... params){ + ClientToolbarState rVal = new ClientToolbarState(parent,params); + //!!WARNING!! from here below should not be touched + //This was generated automatically to properly alert various systems that the btree exists and should be tracked + parent.putData(EntityDataStrings.TREE_CLIENTTOOLBARSTATE, rVal); + Globals.clientSceneWrapper.getScene().registerBehaviorTree(rVal); + Globals.entityValueTrackingService.attachTreeToEntity(parent, BehaviorTreeIdEnums.BTREE_CLIENTTOOLBARSTATE_ID); + return rVal; + } + + /** + *

Automatically generated

+ *

+ * Detatches this tree from the entity. + *

+ * @param entity The entity to detach to + * @param tree The behavior tree to detach + */ + public static void detachTree(Entity entity, BehaviorTree tree){ + Globals.entityValueTrackingService.detatchTreeFromEntity(entity, BehaviorTreeIdEnums.BTREE_CLIENTTOOLBARSTATE_ID); + } + + /** + *

(initially) Automatically generated

+ *

Private constructor to enforce using the attach methods

+ *

+ * Constructor + *

+ * @param parent The parent entity of this tree + * @param params Optional parameters that can be provided when attaching the tree. All custom data required for creating this tree should be passed in this varargs. + */ + private ClientToolbarState(Entity parent, Object ... params){ + this.parent = parent; + this.toolbarData = (ToolbarData)params[0]; + } + + /** + *

+ * Gets the ClientToolbarState of the entity + *

+ * @param entity the entity + * @return The ClientToolbarState + */ + public static ClientToolbarState getClientToolbarState(Entity entity){ + return (ClientToolbarState)entity.getData(EntityDataStrings.TREE_CLIENTTOOLBARSTATE); + } + + /** + *

Automatically generated

+ *

+ * Sets selectedSlot and handles the synchronization logic for it. + *

+ * @param selectedSlot The value to set selectedSlot to. + */ + public void setSelectedSlot(int selectedSlot){ + this.selectedSlot = selectedSlot; + } + + /** + *

Automatically generated

+ *

+ * Gets selectedSlot. + *

+ */ + public int getSelectedSlot(){ + return selectedSlot; + } + + @Override + public void simulate(float deltaTime) { + } + +} diff --git a/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java b/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java index ba1698a0..c66beaee 100644 --- a/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java +++ b/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java @@ -74,11 +74,10 @@ public class ServerEquipState implements BehaviorTree { public void commandAttemptEquip(Entity toEquip, EquipPoint point){ boolean hasEquipped = hasEquippedAtPoint(point.getEquipPointId()); boolean targetIsItem = ItemUtils.isItem(toEquip); - boolean targetIsAttached = AttachUtils.isAttached(toEquip); String equipItemClass = ItemUtils.getEquipClass(toEquip); List pointEquipClassList = point.getEquipClassWhitelist(); boolean itemIsInPointWhitelist = pointEquipClassList.contains(equipItemClass); - if(!hasEquipped && targetIsItem && !targetIsAttached && itemIsInPointWhitelist){ + if(!hasEquipped && targetIsItem && itemIsInPointWhitelist){ serverAttemptEquip(toEquip, point); } } @@ -162,14 +161,21 @@ public class ServerEquipState implements BehaviorTree { //get the parent (typically creature) that contains the in-inventory item Entity containingEntity = ItemUtils.getContainingParent(inInventoryEntity); //actually switch containers - boolean parentHasNaturalInventory = InventoryUtils.hasNaturalInventory(parent); - boolean parentHasEquipInventory = InventoryUtils.hasEquipInventory(parent); - //make sure the switch is possible - if(parentHasEquipInventory && parentHasNaturalInventory){ - //actually switch containers + if(InventoryUtils.hasNaturalInventory(parent)){ UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); - RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); naturalInventory.removeItem(inInventoryEntity); + } + if(InventoryUtils.hasToolbarInventory(parent)){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + if(ServerToolbarState.getServerToolbarState(parent) != null){ + ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(parent); + serverToolbarState.unequip(inWorldItem); + } + toolbarInventory.tryRemoveItem(inInventoryEntity); + } + if(InventoryUtils.hasEquipInventory(parent)){ + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + equipInventory.tryRemoveItem(inInventoryEntity); equipInventory.addItem(point.getEquipPointId(), inInventoryEntity); } //if they're a player, let the player know that the item has moved container @@ -192,7 +198,13 @@ public class ServerEquipState implements BehaviorTree { int equipperId = parent.getId(); String equipPointId = point.getEquipPointId(); int inWorldItemId = inWorldItem.getId(); - NetworkMessage attachMessage = InventoryMessage.constructserverCommandEquipItemMessage(equipperId, equipPointId, inWorldItemId, itemType); + NetworkMessage attachMessage = InventoryMessage.constructserverCommandEquipItemMessage( + equipperId, + InventoryProtocol.INVENTORY_TYPE_EQUIP, + equipPointId, + inWorldItemId, + itemType + ); //actually send the packet dataCell.broadcastNetworkMessage(attachMessage); @@ -282,7 +294,8 @@ public class ServerEquipState implements BehaviorTree { public void serverAttemptUnequip(String pointId){ boolean hasNaturalInventory = InventoryUtils.hasNaturalInventory(parent); boolean hasEquipInventory = InventoryUtils.hasEquipInventory(parent); - if(hasEquipInventory && hasNaturalInventory){ + boolean hasEquipped = hasEquippedAtPoint(pointId); + if(hasEquipped && hasEquipInventory && hasNaturalInventory){ UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); //switch the inventory it's under @@ -295,7 +308,7 @@ public class ServerEquipState implements BehaviorTree { //get datacell ServerDataCell dataCell = DataCellSearchUtils.getEntityDataCell(parent); //broadcast attach entity - NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(parent.getId(), pointId); + NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(parent.getId(), InventoryProtocol.INVENTORY_TYPE_EQUIP, pointId); //actually send the packet dataCell.broadcastNetworkMessage(unequipMessage); //if the parent is a player entity, tell the player about the updated inventory stuff @@ -312,14 +325,6 @@ public class ServerEquipState implements BehaviorTree { controllerPlayer.addMessage(inventoryMessage); } } - // if(ItemUtils.getRealWorldEntity(item) != null){ - // ItemUtils.destroyInWorldItem(ItemUtils.getRealWorldEntity(item)); - // } - // // eject from equip state - // InventoryUtils.attemptDestroyItem(Globals.playerEntity,item); - // // add to unrelational - // sourceInventory.tryRemoveItem(item); - // inventory.addItem(item); } /** diff --git a/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java b/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java new file mode 100644 index 00000000..2e58f287 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java @@ -0,0 +1,373 @@ +package electrosphere.entity.state.equip; + + +import electrosphere.entity.btree.BehaviorTree; +import electrosphere.entity.state.attach.AttachUtils; +import electrosphere.entity.state.block.ServerBlockTree; +import electrosphere.entity.state.gravity.GravityUtils; +import electrosphere.entity.state.inventory.InventoryUtils; +import electrosphere.entity.state.inventory.RelationalInventoryState; +import electrosphere.entity.state.inventory.UnrelationalInventoryState; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.entity.types.item.ItemUtils; +import electrosphere.game.data.creature.type.block.BlockSystem; +import electrosphere.game.data.creature.type.block.BlockVariant; +import electrosphere.game.data.creature.type.equip.EquipPoint; +import electrosphere.game.data.creature.type.equip.ToolbarData; +import electrosphere.game.data.item.type.EquipWhitelist; +import electrosphere.logger.LoggerInterface; + +import java.util.List; + +import org.joml.Vector3d; + +import electrosphere.collision.PhysicsEntityUtils; +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityTags; +import electrosphere.entity.ServerEntityUtils; +import electrosphere.net.synchronization.enums.FieldIdEnums; +import electrosphere.server.datacell.Realm; +import electrosphere.server.datacell.ServerDataCell; +import electrosphere.server.datacell.utils.DataCellSearchUtils; +import electrosphere.net.parser.net.message.InventoryMessage; +import electrosphere.net.parser.net.message.NetworkMessage; +import electrosphere.net.parser.net.message.SynchronizationMessage; +import electrosphere.net.server.player.Player; +import electrosphere.net.server.protocol.InventoryProtocol; +import electrosphere.net.synchronization.enums.BehaviorTreeIdEnums; +import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; +import electrosphere.server.datacell.utils.ServerEntityTagUtils; +import electrosphere.server.utils.ServerScriptUtils; +import electrosphere.net.synchronization.annotation.SyncedField; +import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; + +/** + * The server's toolbar state + */ +@SynchronizedBehaviorTree(name = "serverToolbarState", isServer = true, correspondingTree="clientToolbarState") +public class ServerToolbarState implements BehaviorTree { + + /** + * The selected toolbar slot + */ + @SyncedField + int selectedSlot; + + /** + * The parent + */ + Entity parent; + + /** + * The toolbar data + */ + ToolbarData toolbarData; + + /** + * The real world item + */ + Entity realWorldItem = null; + + /** + * Attempts to add item to toolbar + * @param inInventoryEntity The item to equip + * @param point The point to equip to + */ + public void attemptEquip(Entity inInventoryEntity, int slotId){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + ServerEquipState serverEquipState = ServerEquipState.getEquipState(parent); + boolean hasEquipped = toolbarInventory.hasItemInSlot(slotId + ""); + boolean targetIsItem = ItemUtils.isItem(inInventoryEntity); + if(!hasEquipped && targetIsItem){ + //remove from equip state + if(equipInventory != null && equipInventory.containsItem(inInventoryEntity)){ + serverEquipState.serverAttemptUnequip(equipInventory.getItemSlot(inInventoryEntity)); + equipInventory.tryRemoveItem(inInventoryEntity); + } + + //add to toolbar + toolbarInventory.tryRemoveItem(inInventoryEntity); + toolbarInventory.addItem(slotId + "", inInventoryEntity); + if(slotId == selectedSlot){ + visuallyEquipCurrentSlot(); + } + //if they're a player, let the player know that the item has moved container + if(CreatureUtils.hasControllerPlayerId(parent)){ + //get player + int playerId = CreatureUtils.getControllerPlayerId(parent); + Player controllerPlayer = Globals.playerManager.getPlayerFromId(playerId); + //tell the player they don't have the item anymore + NetworkMessage inventoryMessage = InventoryMessage.constructserverCommandMoveItemContainerMessage( + inInventoryEntity.getId(), + InventoryProtocol.INVENTORY_TYPE_TOOLBAR, + slotId + "" + ); + controllerPlayer.addMessage(inventoryMessage); + } + } + } + + /** + * Visually updates what is equipped at the current slot + */ + public void visuallyEquipCurrentSlot(){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + RelationalInventoryState equipInventoryState = InventoryUtils.getEquipInventory(parent); + Entity inInventoryEntity = toolbarInventory.getItemSlot(selectedSlot + ""); + if(inInventoryEntity != null){ + boolean targetHasWhitelist = ItemUtils.hasEquipList(inInventoryEntity); + String equipItemClass = ItemUtils.getEquipClass(inInventoryEntity); + //hydrate inventory item + String itemType = ItemUtils.getType(inInventoryEntity); + Realm realm = Globals.realmManager.getEntityRealm(parent); + realWorldItem = ItemUtils.serverSpawnBasicItem(realm,new Vector3d(0,0,0),itemType); + //bind in world with in inventory + ItemUtils.setRealWorldEntity(inInventoryEntity, realWorldItem); + + EquipPoint targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getPrimarySlot()); + if(targetPoint.getEquipClassWhitelist() != null && !targetPoint.getEquipClassWhitelist().contains(equipItemClass)){ + targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getCombinedSlot()); + } + + // + //Visual transforms + if(targetHasWhitelist){ + //depends on the type of creature + String parentCreatureId = CreatureUtils.getType(parent); + List whitelist = ItemUtils.getEquipWhitelist(realWorldItem); + for(EquipWhitelist whitelistItem : whitelist){ + if(whitelistItem.getCreatureId().equals(parentCreatureId)){ + //put in map + String modelName = whitelistItem.getModel(); + Globals.assetManager.addModelPathToQueue(modelName); + //attach to parent bone + AttachUtils.serverAttachEntityToEntityAtBone( + parent, + realWorldItem, + targetPoint.getBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorThirdPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationThirdPerson()) + ); + //make uncollidable + if(PhysicsEntityUtils.containsDBody(realWorldItem) && realWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ + Realm inWorldRealm = Globals.realmManager.getEntityRealm(realWorldItem); + inWorldRealm.getCollisionEngine().destroyPhysics(realWorldItem); + } + //make untargetable + ServerEntityTagUtils.removeTagFromEntity(realWorldItem, EntityTags.TARGETABLE); + break; + } + } + } else { + //does not depend on the type of creature + AttachUtils.serverAttachEntityToEntityAtBone( + parent, + realWorldItem, + targetPoint.getBone(), + AttachUtils.getEquipPointVectorOffset(targetPoint.getOffsetVectorThirdPerson()), + AttachUtils.getEquipPointRotationOffset(targetPoint.getOffsetRotationThirdPerson()) + ); + if(PhysicsEntityUtils.containsDBody(realWorldItem) && realWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ + Realm inWorldRealm = Globals.realmManager.getEntityRealm(realWorldItem); + inWorldRealm.getCollisionEngine().destroyPhysics(realWorldItem); + } + ServerEntityTagUtils.removeTagFromEntity(realWorldItem, EntityTags.TARGETABLE); + GravityUtils.serverAttemptDeactivateGravity(realWorldItem); + } + + // + //update block state based on what we have equipped + this.updateBlockVariant(); + + //actually switch containers + boolean parentHasNaturalInventory = InventoryUtils.hasNaturalInventory(parent); + boolean parentHasEquipInventory = InventoryUtils.hasEquipInventory(parent); + //make sure the switch is possible + if(parentHasNaturalInventory){ + UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); + naturalInventory.removeItem(inInventoryEntity); + } + if(parentHasEquipInventory){ + //actually switch containers + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + equipInventory.tryRemoveItem(inInventoryEntity); + } + //get the chunk the equipper is in, and broadcast to that chunk that they equipped the item + //get datacell + ServerDataCell dataCell = DataCellSearchUtils.getEntityDataCell(parent); + //broadcast attach entity + int equipperId = parent.getId(); + int inWorldItemId = realWorldItem.getId(); + NetworkMessage attachMessage = InventoryMessage.constructserverCommandEquipItemMessage( + equipperId, + InventoryProtocol.INVENTORY_TYPE_TOOLBAR, + this.selectedSlot + "", + inWorldItemId, + itemType + ); + //actually send the packet + dataCell.broadcastNetworkMessage(attachMessage); + + //Fire signal to script engine to equip + ServerScriptUtils.fireSignalOnEntity(parent, "equipItem"); + } + } + + /** + * Updates the server block variant based on what item is equipped + */ + private void updateBlockVariant(){ + ServerBlockTree blockTree = ServerBlockTree.getServerBlockTree(parent); + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + RelationalInventoryState equipInventoryState = InventoryUtils.getEquipInventory(parent); + Entity selectedItemEntity = toolbarInventory.getItemSlot(this.selectedSlot + ""); + if(blockTree != null && selectedItemEntity != null){ + + String equipItemClass = ItemUtils.getEquipClass(selectedItemEntity); + EquipPoint targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getPrimarySlot()); + if(!targetPoint.getEquipClassWhitelist().contains(equipItemClass)){ + targetPoint = equipInventoryState.getEquipPointFromSlot(toolbarData.getCombinedSlot()); + } + + BlockSystem blockData = blockTree.getBlockSystem(); + if(selectedItemEntity != null && Globals.gameConfigCurrent.getItemMap().getItem(selectedItemEntity) != null && Globals.gameConfigCurrent.getItemMap().getItem(selectedItemEntity).getItemBlockData() != null){ + BlockVariant blockVariant = blockData.getVariantForPointWithItem(targetPoint.getEquipPointId(),ItemUtils.getEquipClass(selectedItemEntity)); + + //TODO: refactor to allow sending more than one variant at a time + //ie if you have two items equipped and you want to block with both + if(blockVariant != null){ + blockTree.setCurrentBlockVariant(blockVariant.getVariantId()); + } else { + LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Equipped item to equip point that does not have assigned block variant!!")); + } + } + } + } + + /** + * The item to unequip + * @param item The item + */ + public void unequip(Entity item){ + if(item == this.realWorldItem){ + removeVisuals(); + } + if(ItemUtils.getRealWorldEntity(item) != null && ItemUtils.getRealWorldEntity(item) == this.realWorldItem){ + removeVisuals(); + } + } + + /** + * Removes visuals for the currently equipped item if they exist + */ + public void removeVisuals(){ + //destroy in world item + boolean targetHasWhitelist = ItemUtils.hasEquipList(this.realWorldItem); + + // + //Visual transforms + if(targetHasWhitelist){ + } else { + ServerEntityUtils.destroyEntity(this.realWorldItem); + } + + // + //update block state based on what we have equipped + this.updateBlockVariant(); + //tell all clients to unequip the world item + //get datacell + ServerDataCell dataCell = DataCellSearchUtils.getEntityDataCell(parent); + //broadcast attach entity + NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(parent.getId(), InventoryProtocol.INVENTORY_TYPE_TOOLBAR, ""); + //actually send the packet + dataCell.broadcastNetworkMessage(unequipMessage); + } + + /** + *

(initially) Automatically generated

+ *

+ * Attaches this tree to the entity. + *

+ * @param entity The entity to attach to + * @param tree The behavior tree to attach + * @param params Optional parameters that will be provided to the constructor + */ + public static ServerToolbarState attachTree(Entity parent, Object ... params){ + ServerToolbarState rVal = new ServerToolbarState(parent,params); + //!!WARNING!! from here below should not be touched + //This was generated automatically to properly alert various systems that the btree exists and should be tracked + ServerBehaviorTreeUtils.attachBTreeToEntity(parent, rVal); + parent.putData(EntityDataStrings.TREE_SERVERTOOLBARSTATE, rVal); + Globals.entityValueTrackingService.attachTreeToEntity(parent, BehaviorTreeIdEnums.BTREE_SERVERTOOLBARSTATE_ID); + return rVal; + } + + /** + *

Automatically generated

+ *

+ * Detatches this tree from the entity. + *

+ * @param entity The entity to detach to + * @param tree The behavior tree to detach + */ + public static void detachTree(Entity entity, BehaviorTree tree){ + Globals.entityValueTrackingService.detatchTreeFromEntity(entity, BehaviorTreeIdEnums.BTREE_SERVERTOOLBARSTATE_ID); + } + + /** + *

(initially) Automatically generated

+ *

Private constructor to enforce using the attach methods

+ *

+ * Constructor + *

+ * @param parent The parent entity of this tree + * @param params Optional parameters that can be provided when attaching the tree. All custom data required for creating this tree should be passed in this varargs. + */ + private ServerToolbarState(Entity parent, Object ... params){ + this.parent = parent; + this.toolbarData = (ToolbarData)params[0]; + } + + /** + *

+ * Gets the ServerToolbarState of the entity + *

+ * @param entity the entity + * @return The ServerToolbarState + */ + public static ServerToolbarState getServerToolbarState(Entity entity){ + return (ServerToolbarState)entity.getData(EntityDataStrings.TREE_SERVERTOOLBARSTATE); + } + + /** + *

Automatically generated

+ *

+ * Sets selectedSlot and handles the synchronization logic for it. + *

+ * @param selectedSlot The value to set selectedSlot to. + */ + public void setSelectedSlot(int selectedSlot){ + this.selectedSlot = selectedSlot; + if(DataCellSearchUtils.getEntityDataCell(parent) != null){ + DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientIntStateMessage(parent.getId(), BehaviorTreeIdEnums.BTREE_SERVERTOOLBARSTATE_ID, FieldIdEnums.TREE_SERVERTOOLBARSTATE_SYNCEDFIELD_SELECTEDSLOT_ID, selectedSlot)); + } + } + + /** + *

Automatically generated

+ *

+ * Gets selectedSlot. + *

+ */ + public int getSelectedSlot(){ + return selectedSlot; + } + + @Override + public void simulate(float deltaTime) { + } + +} diff --git a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java index c79f546e..a7efac4f 100644 --- a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java +++ b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java @@ -8,7 +8,7 @@ import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.equip.ClientEquipState; -import electrosphere.entity.types.item.ItemUtils; +import electrosphere.entity.state.equip.ClientToolbarState; import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.server.protocol.InventoryProtocol; @@ -57,51 +57,94 @@ public class ClientInventoryState implements BehaviorTree { switch(message.getcontainerType()){ case InventoryProtocol.INVENTORY_TYPE_EQUIP: { Entity target = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()); - boolean isInventoryItem = ItemUtils.itemIsInInventory(target); - boolean parentHasNaturalInventory = InventoryUtils.hasNaturalInventory(parent); - boolean parentHasEquipInventory = InventoryUtils.hasEquipInventory(parent); String equipPointId = message.getequipPointId(); - //check that we can do the transform - if(isInventoryItem && parentHasEquipInventory && parentHasNaturalInventory){ - //switch containers + if(InventoryUtils.hasNaturalInventory(parent)){ UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); - RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); naturalInventory.removeItem(target); + } + if(InventoryUtils.hasEquipInventory(parent)){ + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + equipInventory.tryRemoveItem(target); equipInventory.addItem(equipPointId, target); } + if(ClientEquipState.getClientEquipState(parent) != null){ + ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(parent); + clientEquipState.attemptEquip(target, clientEquipState.getEquipPoint(equipPointId)); + } + if(InventoryUtils.hasToolbarInventory(parent)){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + toolbarInventory.tryRemoveItem(target); + } } break; case InventoryProtocol.INVENTORY_TYPE_NATURAL: { Entity target = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()); - boolean isInventoryItem = ItemUtils.itemIsInInventory(target); - boolean parentHasNaturalInventory = InventoryUtils.hasNaturalInventory(parent); - boolean parentHasEquipInventory = InventoryUtils.hasEquipInventory(parent); - String equipPointId = message.getequipPointId(); - //check that we can do the transform - if(isInventoryItem && parentHasEquipInventory && parentHasNaturalInventory){ - //switch containers + if(InventoryUtils.hasNaturalInventory(parent)){ UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); - RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + naturalInventory.removeItem(target); naturalInventory.addItem(target); - equipInventory.removeItemSlot(equipPointId); + } + if(InventoryUtils.hasEquipInventory(parent)){ + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + if(ClientEquipState.getClientEquipState(parent) != null){ + ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(parent); + clientEquipState.clientTransformUnequipPoint(equipInventory.getItemSlot(target)); + } + equipInventory.tryRemoveItem(target); + } + if(InventoryUtils.hasToolbarInventory(parent)){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + toolbarInventory.tryRemoveItem(target); } } break; case InventoryProtocol.INVENTORY_TYPE_TOOLBAR: { - throw new UnsupportedOperationException("todo"); + Entity target = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()); + if(InventoryUtils.hasNaturalInventory(parent)){ + UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent); + naturalInventory.removeItem(target); + } + if(InventoryUtils.hasEquipInventory(parent)){ + RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(parent); + if(ClientEquipState.getClientEquipState(parent) != null){ + ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(parent); + clientEquipState.clientTransformUnequipPoint(equipInventory.getItemSlot(target)); + } + equipInventory.tryRemoveItem(target); + } + if(InventoryUtils.hasToolbarInventory(parent)){ + RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent); + toolbarInventory.tryRemoveItem(target); + toolbarInventory.addItem("" + message.getequipPointId(), target); + } } } //once we've switched the items around, redraw the inventory to reflect the updated contents WindowUtils.attemptRedrawInventoryWindows(); } break; case SERVERCOMMANDUNEQUIPITEM: { - if(Globals.playerEntity != null && ClientEquipState.hasEquipState(Globals.playerEntity)){ - //unequip the item - ClientEquipState equipState = ClientEquipState.getEquipState(Globals.playerEntity); - Entity entityInSlot = equipState.getEquippedItemAtPoint(message.getequipPointId()); - equipState.clientTransformUnequipPoint(message.getequipPointId()); - //destroy the in-world manifestation of said item - ClientEntityUtils.destroyEntity(entityInSlot); + switch(message.getcontainerType()){ + case InventoryProtocol.INVENTORY_TYPE_NATURAL: { + throw new UnsupportedOperationException("unsupported!"); + } + case InventoryProtocol.INVENTORY_TYPE_EQUIP: { + if(ClientEquipState.hasEquipState(parent)){ + //unequip the item + ClientEquipState equipState = ClientEquipState.getEquipState(parent); + Entity entityInSlot = equipState.getEquippedItemAtPoint(message.getequipPointId()); + equipState.clientTransformUnequipPoint(message.getequipPointId()); + //destroy the in-world manifestation of said item + ClientEntityUtils.destroyEntity(entityInSlot); + } + } break; + case InventoryProtocol.INVENTORY_TYPE_TOOLBAR: { + if(ClientToolbarState.getClientToolbarState(parent) != null){ + ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(parent); + clientToolbarState.unequip(message.getequipPointId()); + } + } break; } } break; + case CLIENTREQUESTADDNATURAL: + case CLIENTREQUESTADDTOOLBAR: case CLIENTREQUESTEQUIPITEM: case CLIENTREQUESTUNEQUIPITEM: case CLIENTREQUESTPERFORMITEMACTION: diff --git a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java index 6575ef0f..3e09cd8e 100644 --- a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java +++ b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java @@ -9,6 +9,7 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ServerEquipState; +import electrosphere.entity.state.equip.ServerToolbarState; import electrosphere.entity.state.gravity.GravityUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; @@ -17,6 +18,7 @@ import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.server.player.Player; +import electrosphere.net.server.protocol.InventoryProtocol; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.utils.DataCellSearchUtils; @@ -203,6 +205,77 @@ public class InventoryUtils { return null; } + /** + * Perform the entity transforms to actually store an item in an inventory, if server this has the side effect of also sending packets on success + * @param creature The creature to store the item in + * @param item The item to store + * @return The in-inventory item + */ + public static Entity serverAddToNatural(Entity creature, Entity item){ + boolean creatureIsCreature = CreatureUtils.isCreature(creature); + boolean itemIsItem = ItemUtils.isItem(item); + boolean hasInventory = hasNaturalInventory(creature); + //check if the item is already in an inventory + boolean itemIsInInventory = ItemUtils.itemIsInInventory(item); + if(creatureIsCreature && itemIsItem && hasInventory){ + //get inventory + //for the moment we're just gonna get natural inventory + //later we'll need to search through all creature inventories to find the item + UnrelationalInventoryState inventory = getNaturalInventory(creature); + //destroy in-world entity and create in-inventory item + //we're doing this so that we're not constantly sending networking messages for invisible entities attached to the player + Entity inventoryItem = item; + boolean sendCreatePacket = false; + if(!itemIsInInventory){ + sendCreatePacket = true; + inventoryItem = ItemUtils.serverRecreateContainerItem(item, creature); + ServerEntityUtils.destroyEntity(item); + } + if(InventoryUtils.hasEquipInventory(creature) && InventoryUtils.getEquipInventory(creature).containsItem(item)){ + InventoryUtils.getEquipInventory(creature).tryRemoveItem(item); + } + if(InventoryUtils.hasToolbarInventory(creature) && InventoryUtils.getToolbarInventory(creature).containsItem(item)){ + if(ServerToolbarState.getServerToolbarState(creature) != null){ + ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(creature); + serverToolbarState.unequip(item); + } + InventoryUtils.getToolbarInventory(creature).tryRemoveItem(item); + } + //store item in inventory + inventory.addItem(inventoryItem); + //set item containing parent + ItemUtils.setContainingParent(inventoryItem, creature); + //tell controlling player that they have an item in their inventory + if(CreatureUtils.hasControllerPlayerId(creature)){ + //get the player + int controllerPlayerID = CreatureUtils.getControllerPlayerId(creature); + Player controllerPlayer = Globals.playerManager.getPlayerFromId(controllerPlayerID); + //send message + if(sendCreatePacket){ + controllerPlayer.addMessage(InventoryMessage.constructaddItemToInventoryMessage(inventoryItem.getId(), ItemUtils.getType(inventoryItem))); + } else { + controllerPlayer.addMessage(InventoryMessage.constructserverCommandMoveItemContainerMessage(inventoryItem.getId(), InventoryProtocol.INVENTORY_TYPE_NATURAL, "")); + } + } + //alert script engine + ServerScriptUtils.fireSignalOnEntity(creature, "itemAddToNatural", item.getId(), inventoryItem.getId()); + return inventoryItem; + } + return null; + } + + /** + * Asks the server to add the item to the player's natural inventory + * @param item The item to store + */ + public static void clientAddToNatural(Entity item){ + //tell the server we want to try the transform + NetworkMessage requestPickupMessage = InventoryMessage.constructclientRequestAddNaturalMessage( + Globals.clientSceneWrapper.mapClientToServerId(item.getId()) + ); + Globals.clientConnection.queueOutgoingMessage(requestPickupMessage); + } + /** * Attempts to store the in-world item entity in a creature inventory container * @param creature the creature which has a natural inventory @@ -217,17 +290,6 @@ public class InventoryUtils { Globals.clientConnection.queueOutgoingMessage(requestPickupMessage); } - /** - * Attempts to store the in-world item entity in a creature inventory container - * @param creature the creature which has a natural inventory - * @param item the in-world item entity to store - * @return The in-inventory entity if it succeeded, null otherwise - */ - public static Entity serverAttemptStoreItem(Entity creature, Entity item){ - //immediately attempt the transform - return serverAttemptStoreItemTransform(creature,item); - } - /** * Places an item of provided type in the parent container's natural inventory * @param parentContainer The entity (typically a creature) which will receive the item in their natural inventory @@ -300,7 +362,7 @@ public class InventoryUtils { //get closest chunk ServerDataCell dataCell = realm.getEntityDataCellMapper().getEntityDataCell(realWorldItem); //broadcast destroy item - NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(creature.getId(), inventorySlot); + NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(creature.getId(), InventoryProtocol.INVENTORY_TYPE_EQUIP, inventorySlot); dataCell.broadcastNetworkMessage(unequipMessage); } //drop item diff --git a/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java index f33c6a83..d2bee4f1 100644 --- a/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java +++ b/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java @@ -213,4 +213,13 @@ public class RelationalInventoryState { return null; } + /** + * Checks if this inventory contains this item + * @param item The item + * @return true if it is inside this inventory, false otherwise + */ + public boolean containsItem(Entity item){ + return this.items.values().contains(item); + } + } diff --git a/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java index 15d074f5..5551466d 100644 --- a/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java +++ b/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java @@ -5,6 +5,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import electrosphere.entity.Entity; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.equip.ServerEquipState; +import electrosphere.entity.state.equip.ServerToolbarState; import electrosphere.game.data.creature.type.equip.EquipPoint; import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.server.datacell.utils.EntityLookupUtils; @@ -35,13 +36,13 @@ public class ServerInventoryState implements BehaviorTree { for(InventoryMessage message : networkMessageQueue){ networkMessageQueue.remove(message); switch(message.getMessageSubtype()){ - case ADDITEMTOINVENTORY: - InventoryUtils.serverAttemptStoreItem(parent, EntityLookupUtils.getEntityById(message.getentityId())); - break; - case REMOVEITEMFROMINVENTORY: + case ADDITEMTOINVENTORY: { + InventoryUtils.serverAttemptStoreItemTransform(parent, EntityLookupUtils.getEntityById(message.getentityId())); + } break; + case REMOVEITEMFROMINVENTORY: { InventoryUtils.serverAttemptEjectItem(parent, EntityLookupUtils.getEntityById(message.getentityId())); - break; - case CLIENTREQUESTEQUIPITEM:{ + } break; + case CLIENTREQUESTEQUIPITEM: { //item to equip Entity target = EntityLookupUtils.getEntityById(message.getentityId()); //perform transform if it makes sense @@ -52,7 +53,7 @@ public class ServerInventoryState implements BehaviorTree { } } break; - case CLIENTREQUESTUNEQUIPITEM:{ + case CLIENTREQUESTUNEQUIPITEM: { //make sure can unequip if(InventoryUtils.hasEquipInventory(parent) && InventoryUtils.hasNaturalInventory(parent) && ServerEquipState.hasEquipState(parent)){ ServerEquipState equipState = ServerEquipState.getEquipState(parent); @@ -62,8 +63,15 @@ public class ServerInventoryState implements BehaviorTree { //tell player } } - } - break; + } break; + case CLIENTREQUESTADDNATURAL: { + InventoryUtils.serverAddToNatural(parent, EntityLookupUtils.getEntityById(message.getentityId())); + } break; + case CLIENTREQUESTADDTOOLBAR: { + Entity itemEnt = EntityLookupUtils.getEntityById(message.getentityId()); + ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(parent); + serverToolbarState.attemptEquip(itemEnt, message.gettoolbarId()); + } break; case CLIENTREQUESTPERFORMITEMACTION: case SERVERCOMMANDUNEQUIPITEM: case SERVERCOMMANDEQUIPITEM: diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java index 89eb9b00..0bd9ea42 100644 --- a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java +++ b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java @@ -22,7 +22,9 @@ import electrosphere.entity.state.block.ClientBlockTree; import electrosphere.entity.state.block.ServerBlockTree; import electrosphere.entity.state.client.particle.ClientParticleEmitterComponent; import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.entity.state.equip.ClientToolbarState; import electrosphere.entity.state.equip.ServerEquipState; +import electrosphere.entity.state.equip.ServerToolbarState; import electrosphere.entity.state.gravity.ClientGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree; import electrosphere.entity.state.hitbox.HitboxCollectionState; @@ -281,6 +283,9 @@ public class CommonEntityUtils { if(rawType.getEquipPoints() != null && rawType.getEquipPoints().size() > 0){ ClientEquipState.attachTree(entity, rawType.getEquipPoints()); InventoryUtils.setEquipInventory(entity, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); + } + if(rawType.getToolbarData() != null){ + ClientToolbarState.attachTree(entity, rawType.getToolbarData()); InventoryUtils.setToolbarInventory(entity, RelationalInventoryState.buildToolbarInventory()); } if(rawType.getBlockSystem() != null){ @@ -557,6 +562,9 @@ public class CommonEntityUtils { if(rawType.getEquipPoints() != null && rawType.getEquipPoints().size() > 0){ ServerEquipState.attachTree(entity, rawType.getEquipPoints()); InventoryUtils.setEquipInventory(entity, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); + } + if(rawType.getToolbarData() != null){ + ServerToolbarState.attachTree(entity, rawType.getToolbarData()); InventoryUtils.setToolbarInventory(entity, RelationalInventoryState.buildToolbarInventory()); } diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureTemplate.java b/src/main/java/electrosphere/entity/types/creature/CreatureTemplate.java index df9eb85f..4255d374 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureTemplate.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureTemplate.java @@ -28,6 +28,11 @@ public class CreatureTemplate { */ private CreatureEquipData equipData = new CreatureEquipData(); + /** + * The toolbar data for the creature + */ + private CreatureToolbarData toolbarData = new CreatureToolbarData(); + /** * The collection of synchronized values */ @@ -87,6 +92,14 @@ public class CreatureTemplate { return this.equipData; } + /** + * Gets the toolbar data for the template + * @return The toolbar data for the template + */ + public CreatureToolbarData getCreatureToolbarData(){ + return this.toolbarData; + } + /** * Gets the state collection for the creature * @return The collection of synchronized values diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureToolbarData.java b/src/main/java/electrosphere/entity/types/creature/CreatureToolbarData.java new file mode 100644 index 00000000..b0234b58 --- /dev/null +++ b/src/main/java/electrosphere/entity/types/creature/CreatureToolbarData.java @@ -0,0 +1,92 @@ +package electrosphere.entity.types.creature; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * The data about what is in the creature's toolbar + */ +public class CreatureToolbarData { + + //Map of equip slot -> item definition + Map slotToItemMap = new HashMap(); + + /** + * Gets the slots in the equip data + * @return The set of equip slot ids + */ + public Set getSlots(){ + return slotToItemMap.keySet(); + } + + /** + * Gets the item occupying a given slot + * @param slotId The id of the slot + * @return The item definition + */ + public ToolbarItem getSlotItem(String slotId){ + return slotToItemMap.get(slotId); + } + + /** + * Sets a slot as containing an item type + * @param slotId The slot + * @param itemType The item definition + */ + public void setSlotItem(String slotId, ToolbarItem itemType){ + slotToItemMap.put(slotId, itemType); + } + + /** + * Clears the equip data + */ + public void clear(){ + slotToItemMap.clear(); + } + + /** + * An item that is equipped + */ + public static class ToolbarItem { + + /** + * The type of the item + */ + String itemType; + + /** + * The entity id of the item + */ + int entityId; + + /** + * Constructor + * @param entityId The id of the item entity + * @param itemType The type of the item + */ + public ToolbarItem(int entityId, String itemType){ + this.entityId = entityId; + this.itemType = itemType; + } + + /** + * The type of the item + * @return The type + */ + public String getItemType(){ + return itemType; + } + + /** + * The id of the entity that is this item + * @return The id + */ + public int getEntityId(){ + return entityId; + } + + } + + +} diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 81c46fb0..0d4a74d0 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -14,13 +14,18 @@ import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.attack.ClientAttackTree; import electrosphere.entity.state.attack.ServerAttackTree; import electrosphere.entity.state.client.firstPerson.FirstPersonTree; +import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.entity.state.equip.ClientToolbarState; import electrosphere.entity.state.equip.ServerEquipState; +import electrosphere.entity.state.equip.ServerToolbarState; import electrosphere.entity.state.idle.ClientIdleTree; +import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; import electrosphere.entity.types.EntityTypes.EntityType; import electrosphere.entity.types.common.CommonEntityUtils; import electrosphere.entity.types.creature.CreatureEquipData.EquippedItem; +import electrosphere.entity.types.creature.CreatureToolbarData.ToolbarItem; import electrosphere.entity.types.item.ItemUtils; import electrosphere.game.data.creature.type.CreatureData; import electrosphere.game.data.creature.type.visualattribute.AttributeVariant; @@ -34,6 +39,7 @@ import electrosphere.net.synchronization.transport.StateCollection; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.ActorStaticMorph; import electrosphere.server.datacell.Realm; +import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.poseactor.PoseActor; import electrosphere.util.Utilities; @@ -137,6 +143,50 @@ public class CreatureUtils { return rVal; } + /** + * Applies a creature template's item transforms to the creature (ie attaches items that should be attached) + * @param realm The realm + * @param creature The creature + * @param template The template + */ + public static void clientApplyTemplate(Entity creature, CreatureTemplate template){ + // + //must happen after the player is attached to the entity, or server won't send packet to add item to player's entity + //now that creature has been spawned, need to create all attached items + if(template != null){ + if(template.getCreatureEquipData() != null && template.getCreatureEquipData().getSlots() != null){ + for(String equipSlotId : template.getCreatureEquipData().getSlots()){ + + //add the item to the creature's inventory + EquippedItem itemDefinition = template.getCreatureEquipData().getSlotItem(equipSlotId); + Entity itemInInventory = InventoryUtils.clientConstructInInventoryItem(creature,itemDefinition.getItemType()); + + //equip the item to the slot defined in the template + ClientEquipState clientEquipState = ClientEquipState.getEquipState(creature); + clientEquipState.attemptEquip(itemInInventory, clientEquipState.getEquipPoint(equipSlotId)); + + //map the constructed item to its server id + Globals.clientSceneWrapper.mapIdToId(itemInInventory.getId(), itemDefinition.getEntityId()); + } + } + if(template.getCreatureToolbarData() != null && template.getCreatureToolbarData().getSlots() != null){ + for(String equipSlotId : template.getCreatureToolbarData().getSlots()){ + + //add the item to the creature's inventory + ToolbarItem itemDefinition = template.getCreatureToolbarData().getSlotItem(equipSlotId); + Entity itemInInventory = InventoryUtils.clientConstructInInventoryItem(creature,itemDefinition.getItemType()); + + //equip the item to the slot defined in the template + ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(creature); + clientToolbarState.attemptAddToToolbar(itemInInventory, Integer.parseInt(equipSlotId)); + + //map the constructed item to its server id + Globals.clientSceneWrapper.mapIdToId(itemInInventory.getId(), itemDefinition.getEntityId()); + } + } + } + } + /** * Spawns a server-side creature @@ -250,6 +300,50 @@ public class CreatureUtils { return rVal; } + /** + * Applies a creature template's item transforms to the creature (ie attaches items that should be attached) + * @param realm The realm + * @param creature The creature + * @param template The template + */ + public static void serverApplyTemplate(Realm realm, Entity creature, CreatureTemplate template){ + // + //must happen after the player is attached to the entity, or server won't send packet to add item to player's entity + //now that creature has been spawned, need to create all attached items + if(template != null){ + if(template.getCreatureEquipData() != null && template.getCreatureEquipData().getSlots() != null){ + for(String equipSlotId : template.getCreatureEquipData().getSlots()){ + + //spawn the item in the world + EquippedItem itemDefinition = template.getCreatureEquipData().getSlotItem(equipSlotId); + Entity itemInWorld = ItemUtils.serverSpawnBasicItem(realm, EntityUtils.getPosition(creature), itemDefinition.getItemType()); + + //add the item to the creature's inventory + Entity itemInInventory = InventoryUtils.serverAttemptStoreItemTransform(creature, EntityLookupUtils.getEntityById(itemInWorld.getId())); + + //equip the item to the slot defined in the template + ServerEquipState serverEquipState = ServerEquipState.getEquipState(creature); + serverEquipState.commandAttemptEquip(itemInInventory,serverEquipState.getEquipPoint(equipSlotId)); + } + } + if(template.getCreatureToolbarData() != null && template.getCreatureToolbarData().getSlots() != null){ + for(String equipSlotId : template.getCreatureToolbarData().getSlots()){ + + //spawn the item in the world + ToolbarItem itemDefinition = template.getCreatureToolbarData().getSlotItem(equipSlotId); + Entity itemInWorld = ItemUtils.serverSpawnBasicItem(realm, EntityUtils.getPosition(creature), itemDefinition.getItemType()); + + //add the item to the creature's inventory + Entity itemInInventory = InventoryUtils.serverAttemptStoreItemTransform(creature, EntityLookupUtils.getEntityById(itemInWorld.getId())); + + //equip the item to the slot defined in the template + ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(creature); + serverToolbarState.attemptEquip(itemInInventory, Integer.parseInt(equipSlotId)); + } + } + } + } + /** * Creates a viewmodel entity on the client side * @param type The type of creature diff --git a/src/main/java/electrosphere/game/data/common/CommonEntityType.java b/src/main/java/electrosphere/game/data/common/CommonEntityType.java index 4d1d7938..207377b2 100644 --- a/src/main/java/electrosphere/game/data/common/CommonEntityType.java +++ b/src/main/java/electrosphere/game/data/common/CommonEntityType.java @@ -14,6 +14,7 @@ import electrosphere.game.data.creature.type.attack.AttackMoveResolver; import electrosphere.game.data.creature.type.block.BlockSystem; import electrosphere.game.data.creature.type.bonegroups.BoneGroup; import electrosphere.game.data.creature.type.equip.EquipPoint; +import electrosphere.game.data.creature.type.equip.ToolbarData; import electrosphere.game.data.creature.type.movement.MovementSystem; import electrosphere.game.data.creature.type.rotator.RotatorSystem; import electrosphere.game.data.foliage.type.AmbientAudio; @@ -56,6 +57,11 @@ public class CommonEntityType { */ List equipPoints; + /** + * The data for the toolbar + */ + ToolbarData toolbarData; + /** * The collidable used for this creature type */ @@ -206,6 +212,14 @@ public class CommonEntityType { return equipPoints; } + /** + * Gets the toolbar data + * @return The toolbar data + */ + public ToolbarData getToolbarData(){ + return toolbarData; + } + /** * Sets the attack move resolver for this creature type * @param resolver The resolver diff --git a/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java b/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java index c551a05d..0272b868 100644 --- a/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java +++ b/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java @@ -67,6 +67,11 @@ public class EquipPoint { */ List subPoints; + /** + * Controls whether this is a toolbar slot or an equipment slot + */ + boolean isToolbarSlot; + @@ -215,5 +220,13 @@ public class EquipPoint { public List getSubPoints(){ return this.subPoints; } + + /** + * Gets whether this is a toolbar slot or not + * @return true for toolbar slot, false otherwise + */ + public boolean isToolbarSlot(){ + return this.isToolbarSlot; + } } diff --git a/src/main/java/electrosphere/game/data/creature/type/equip/ToolbarData.java b/src/main/java/electrosphere/game/data/creature/type/equip/ToolbarData.java new file mode 100644 index 00000000..64efc4c2 --- /dev/null +++ b/src/main/java/electrosphere/game/data/creature/type/equip/ToolbarData.java @@ -0,0 +1,34 @@ +package electrosphere.game.data.creature.type.equip; + +/** + * Data about the toolbar + */ +public class ToolbarData { + + /** + * The id for the primary toolbar equip point + */ + String primarySlot; + + /** + * The id for the combined toolbar equip point + */ + String combinedSlot; + + /** + * Gets the id for the primary toolbar equip point + * @return The id for the primary toolbar equip point + */ + public String getPrimarySlot(){ + return primarySlot; + } + + /** + * Gets the id for the combined toolbar equip point + * @return The id for the combined toolbar equip point + */ + public String getCombinedSlot(){ + return combinedSlot; + } + +} diff --git a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java index 4527d320..3dfa4b61 100644 --- a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java @@ -10,13 +10,10 @@ import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.state.attach.AttachUtils; -import electrosphere.entity.state.equip.ClientEquipState; -import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree; import electrosphere.entity.types.EntityTypes; import electrosphere.entity.types.EntityTypes.EntityType; import electrosphere.entity.types.common.CommonEntityUtils; -import electrosphere.entity.types.creature.CreatureEquipData.EquippedItem; import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.foliage.FoliageUtils; @@ -211,21 +208,7 @@ public class EntityProtocol implements ClientProtocolTemplate { ); Globals.clientSceneWrapper.mapIdToId(newlySpawnedEntity.getId(), message.getentityID()); //if the creature template includes an equip section, spawn all the equipped items - if(template != null && template.getCreatureEquipData() != null && template.getCreatureEquipData().getSlots() != null){ - for(String equipSlotId : template.getCreatureEquipData().getSlots()){ - - //add the item to the creature's inventory - EquippedItem itemDefinition = template.getCreatureEquipData().getSlotItem(equipSlotId); - Entity itemInInventory = InventoryUtils.clientConstructInInventoryItem(newlySpawnedEntity,itemDefinition.getItemType()); - - //equip the item to the slot defined in the template - ClientEquipState clientEquipState = ClientEquipState.getEquipState(newlySpawnedEntity); - clientEquipState.attemptEquip(itemInInventory, clientEquipState.getEquipPoint(equipSlotId)); - - //map the constructed item to its server id - Globals.clientSceneWrapper.mapIdToId(itemInInventory.getId(), itemDefinition.getEntityId()); - } - } + CreatureUtils.clientApplyTemplate(newlySpawnedEntity, template); //apply state synchronization if present if(template != null && template.getStateCollection() != null && template.getStateCollection().getValues() != null){ StateCollection.applyStateCollection(newlySpawnedEntity, template.getStateCollection()); diff --git a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java index 29e92683..d858a4ae 100644 --- a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java @@ -3,6 +3,7 @@ package electrosphere.net.client.protocol; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.entity.state.equip.ClientToolbarState; import electrosphere.entity.state.inventory.ClientInventoryState; import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.game.data.creature.type.equip.EquipPoint; @@ -41,14 +42,27 @@ public class InventoryProtocol implements ClientProtocolTemplate= TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE){ + return true; + } else { + return false; + } + case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL: + if(byteBuffer.getRemaining() >= TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE){ + return true; + } else { + return false; + } case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION: return InventoryMessage.canParseclientRequestPerformItemActionMessage(byteBuffer); } @@ -261,33 +284,36 @@ public class InventoryMessage extends NetworkMessage { if(currentStreamLength < 6){ return false; } - int equipPointIdSize = 0; if(currentStreamLength < 10){ return false; - } else { - temporaryByteQueue.add(byteBuffer.peek(6 + 0)); - temporaryByteQueue.add(byteBuffer.peek(6 + 1)); - temporaryByteQueue.add(byteBuffer.peek(6 + 2)); - temporaryByteQueue.add(byteBuffer.peek(6 + 3)); - equipPointIdSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); } - if(currentStreamLength < 10 + equipPointIdSize){ + int equipPointIdSize = 0; + if(currentStreamLength < 14){ return false; + } else { + temporaryByteQueue.add(byteBuffer.peek(10 + 0)); + temporaryByteQueue.add(byteBuffer.peek(10 + 1)); + temporaryByteQueue.add(byteBuffer.peek(10 + 2)); + temporaryByteQueue.add(byteBuffer.peek(10 + 3)); + equipPointIdSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); } if(currentStreamLength < 14 + equipPointIdSize){ return false; } + if(currentStreamLength < 18 + equipPointIdSize){ + return false; + } int itemTemplateSize = 0; - if(currentStreamLength < 18){ + if(currentStreamLength < 22){ return false; } else { - temporaryByteQueue.add(byteBuffer.peek(14 + equipPointIdSize + 0)); - temporaryByteQueue.add(byteBuffer.peek(14 + equipPointIdSize + 1)); - temporaryByteQueue.add(byteBuffer.peek(14 + equipPointIdSize + 2)); - temporaryByteQueue.add(byteBuffer.peek(14 + equipPointIdSize + 3)); + temporaryByteQueue.add(byteBuffer.peek(18 + equipPointIdSize + 0)); + temporaryByteQueue.add(byteBuffer.peek(18 + equipPointIdSize + 1)); + temporaryByteQueue.add(byteBuffer.peek(18 + equipPointIdSize + 2)); + temporaryByteQueue.add(byteBuffer.peek(18 + equipPointIdSize + 3)); itemTemplateSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); } - if(currentStreamLength < 18 + equipPointIdSize + itemTemplateSize){ + if(currentStreamLength < 22 + equipPointIdSize + itemTemplateSize){ return false; } return true; @@ -297,15 +323,17 @@ public class InventoryMessage extends NetworkMessage { InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM); stripPacketHeader(byteBuffer); rVal.setequipperId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setcontainerType(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setequipPointId(ByteStreamUtils.popStringFromByteQueue(byteBuffer)); rVal.setentityId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setitemTemplate(ByteStreamUtils.popStringFromByteQueue(byteBuffer)); return rVal; } - public static InventoryMessage constructserverCommandEquipItemMessage(int equipperId,String equipPointId,int entityId,String itemTemplate){ + public static InventoryMessage constructserverCommandEquipItemMessage(int equipperId,int containerType,String equipPointId,int entityId,String itemTemplate){ InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM); rVal.setequipperId(equipperId); + rVal.setcontainerType(containerType); rVal.setequipPointId(equipPointId); rVal.setentityId(entityId); rVal.setitemTemplate(itemTemplate); @@ -319,17 +347,20 @@ public class InventoryMessage extends NetworkMessage { if(currentStreamLength < 6){ return false; } - int equipPointIdSize = 0; if(currentStreamLength < 10){ return false; + } + int equipPointIdSize = 0; + if(currentStreamLength < 14){ + return false; } else { - temporaryByteQueue.add(byteBuffer.peek(6 + 0)); - temporaryByteQueue.add(byteBuffer.peek(6 + 1)); - temporaryByteQueue.add(byteBuffer.peek(6 + 2)); - temporaryByteQueue.add(byteBuffer.peek(6 + 3)); + temporaryByteQueue.add(byteBuffer.peek(10 + 0)); + temporaryByteQueue.add(byteBuffer.peek(10 + 1)); + temporaryByteQueue.add(byteBuffer.peek(10 + 2)); + temporaryByteQueue.add(byteBuffer.peek(10 + 3)); equipPointIdSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); } - if(currentStreamLength < 10 + equipPointIdSize){ + if(currentStreamLength < 14 + equipPointIdSize){ return false; } return true; @@ -339,13 +370,15 @@ public class InventoryMessage extends NetworkMessage { InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM); stripPacketHeader(byteBuffer); rVal.setequipperId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setcontainerType(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setequipPointId(ByteStreamUtils.popStringFromByteQueue(byteBuffer)); return rVal; } - public static InventoryMessage constructserverCommandUnequipItemMessage(int equipperId,String equipPointId){ + public static InventoryMessage constructserverCommandUnequipItemMessage(int equipperId,int containerType,String equipPointId){ InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM); rVal.setequipperId(equipperId); + rVal.setcontainerType(containerType); rVal.setequipPointId(equipPointId); rVal.serialize(); return rVal; @@ -384,6 +417,36 @@ public class InventoryMessage extends NetworkMessage { return rVal; } + public static InventoryMessage parseclientRequestAddToolbarMessage(CircularByteBuffer byteBuffer){ + InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR); + stripPacketHeader(byteBuffer); + rVal.setentityId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.settoolbarId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + return rVal; + } + + public static InventoryMessage constructclientRequestAddToolbarMessage(int entityId,int toolbarId){ + InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR); + rVal.setentityId(entityId); + rVal.settoolbarId(toolbarId); + rVal.serialize(); + return rVal; + } + + public static InventoryMessage parseclientRequestAddNaturalMessage(CircularByteBuffer byteBuffer){ + InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL); + stripPacketHeader(byteBuffer); + rVal.setentityId(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + return rVal; + } + + public static InventoryMessage constructclientRequestAddNaturalMessage(int entityId){ + InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL); + rVal.setentityId(entityId); + rVal.serialize(); + return rVal; + } + public static boolean canParseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){ int currentStreamLength = byteBuffer.getRemaining(); List temporaryByteQueue = new LinkedList(); @@ -505,7 +568,7 @@ public class InventoryMessage extends NetworkMessage { } break; case SERVERCOMMANDEQUIPITEM: - rawBytes = new byte[2+4+4+equipPointId.length()+4+4+itemTemplate.length()]; + rawBytes = new byte[2+4+4+4+equipPointId.length()+4+4+itemTemplate.length()]; //message header rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY; //entity messaage header @@ -514,29 +577,33 @@ public class InventoryMessage extends NetworkMessage { for(int i = 0; i < 4; i++){ rawBytes[2+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(equipPointId.length()); + intValues = ByteStreamUtils.serializeIntToBytes(containerType); for(int i = 0; i < 4; i++){ rawBytes[6+i] = intValues[i]; } + intValues = ByteStreamUtils.serializeIntToBytes(equipPointId.length()); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } stringBytes = equipPointId.getBytes(); for(int i = 0; i < equipPointId.length(); i++){ - rawBytes[10+i] = stringBytes[i]; + rawBytes[14+i] = stringBytes[i]; } intValues = ByteStreamUtils.serializeIntToBytes(entityId); for(int i = 0; i < 4; i++){ - rawBytes[10+equipPointId.length()+i] = intValues[i]; + rawBytes[14+equipPointId.length()+i] = intValues[i]; } intValues = ByteStreamUtils.serializeIntToBytes(itemTemplate.length()); for(int i = 0; i < 4; i++){ - rawBytes[14+equipPointId.length()+i] = intValues[i]; + rawBytes[18+equipPointId.length()+i] = intValues[i]; } stringBytes = itemTemplate.getBytes(); for(int i = 0; i < itemTemplate.length(); i++){ - rawBytes[18+equipPointId.length()+i] = stringBytes[i]; + rawBytes[22+equipPointId.length()+i] = stringBytes[i]; } break; case SERVERCOMMANDUNEQUIPITEM: - rawBytes = new byte[2+4+4+equipPointId.length()]; + rawBytes = new byte[2+4+4+4+equipPointId.length()]; //message header rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY; //entity messaage header @@ -545,13 +612,17 @@ public class InventoryMessage extends NetworkMessage { for(int i = 0; i < 4; i++){ rawBytes[2+i] = intValues[i]; } - intValues = ByteStreamUtils.serializeIntToBytes(equipPointId.length()); + intValues = ByteStreamUtils.serializeIntToBytes(containerType); for(int i = 0; i < 4; i++){ rawBytes[6+i] = intValues[i]; } + intValues = ByteStreamUtils.serializeIntToBytes(equipPointId.length()); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } stringBytes = equipPointId.getBytes(); for(int i = 0; i < equipPointId.length(); i++){ - rawBytes[10+i] = stringBytes[i]; + rawBytes[14+i] = stringBytes[i]; } break; case CLIENTREQUESTUNEQUIPITEM: @@ -569,6 +640,32 @@ public class InventoryMessage extends NetworkMessage { rawBytes[6+i] = stringBytes[i]; } break; + case CLIENTREQUESTADDTOOLBAR: + rawBytes = new byte[2+4+4]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY; + //entity messaage header + rawBytes[1] = TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR; + intValues = ByteStreamUtils.serializeIntToBytes(entityId); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(toolbarId); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + break; + case CLIENTREQUESTADDNATURAL: + rawBytes = new byte[2+4]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY; + //entity messaage header + rawBytes[1] = TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL; + intValues = ByteStreamUtils.serializeIntToBytes(entityId); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + break; case CLIENTREQUESTPERFORMITEMACTION: rawBytes = new byte[2+4+equipPointId.length()+4+4]; //message header diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index 5350c316..5d4262c7 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -312,6 +312,16 @@ COMBAT_MESSAGE, rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer); } break; + case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR: + if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = InventoryMessage.parseclientRequestAddToolbarMessage(byteBuffer); + } + break; + case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL: + if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = InventoryMessage.parseclientRequestAddNaturalMessage(byteBuffer); + } + break; case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION: if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){ rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer); diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index 6b08b092..9abe1e1e 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -136,11 +136,15 @@ Message categories public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDEQUIPITEM = 4; public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDUNEQUIPITEM = 5; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM = 6; - public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 7; + public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR = 7; + public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL = 8; + public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 9; /* Inventory packet sizes */ public static final byte INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY_SIZE = 6; + public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE = 10; + public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE = 6; /* Synchronization subcategories */ diff --git a/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java b/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java index 17f11c38..0dd1c9ad 100644 --- a/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java @@ -61,7 +61,19 @@ public class InventoryProtocol implements ServerProtocolTemplate