/* * Copyright 2016 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.logic.inventory; import org.terasology.logic.characters.events.DeathEvent; import org.terasology.physics.HitResult; import org.terasology.physics.Physics; import org.terasology.physics.StandardCollisionGroup; import org.terasology.utilities.Assets; import org.terasology.audio.events.PlaySoundForOwnerEvent; import org.terasology.engine.Time; import org.terasology.entitySystem.entity.EntityManager; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.entitySystem.entity.lifecycleEvents.OnAddedComponent; import org.terasology.entitySystem.event.ReceiveEvent; import org.terasology.entitySystem.systems.BaseComponentSystem; import org.terasology.entitySystem.systems.RegisterMode; import org.terasology.entitySystem.systems.RegisterSystem; import org.terasology.input.binds.inventory.DropItemButton; import org.terasology.input.binds.inventory.ToolbarNextButton; import org.terasology.input.binds.inventory.ToolbarPrevButton; import org.terasology.input.binds.inventory.ToolbarSlotButton; import org.terasology.logic.characters.CharacterComponent; import org.terasology.logic.characters.CharacterHeldItemComponent; import org.terasology.logic.characters.events.ChangeHeldItemRequest; import org.terasology.logic.inventory.events.ChangeSelectedInventorySlotRequest; import org.terasology.logic.inventory.events.DropItemEvent; import org.terasology.logic.inventory.events.DropItemRequest; import org.terasology.logic.inventory.events.GiveItemEvent; import org.terasology.logic.inventory.events.InventorySlotChangedEvent; import org.terasology.logic.location.LocationComponent; import org.terasology.logic.players.LocalPlayer; import org.terasology.math.geom.Vector3f; import org.terasology.network.NetworkSystem; import org.terasology.physics.events.ImpulseEvent; import org.terasology.registry.In; import org.terasology.rendering.nui.NUIManager; import org.terasology.rendering.nui.layers.hud.InventoryHud; @RegisterSystem public class CharacterInventorySystem extends BaseComponentSystem { @In private LocalPlayer localPlayer; @In private Time time; @In private NUIManager nuiManager; @In private InventoryManager inventoryManager; @In private NetworkSystem networkSystem; @In private EntityManager entityManager; @In private Physics physics; private long lastInteraction; private long lastTimeThrowInteraction; @ReceiveEvent(netFilter = RegisterMode.AUTHORITY) public void ensureTransferSlotIsCreated(OnAddedComponent event, EntityRef entityRef, CharacterComponent characterComponent) { EntityRef transferSlot = entityManager.create("core:transferSlot"); characterComponent.movingItem = transferSlot; entityRef.saveComponent(characterComponent); } @ReceiveEvent(netFilter = RegisterMode.AUTHORITY) public void syncSelectedSlotWithHeldItem(InventorySlotChangedEvent event, EntityRef entityRef, SelectedInventorySlotComponent selectedInventorySlotComponent) { if (selectedInventorySlotComponent.slot == event.getSlot()) { entityRef.send(new ChangeHeldItemRequest(event.getNewItem())); } } @ReceiveEvent(netFilter = RegisterMode.AUTHORITY) public void onChangeSelectedInventorySlotRequested(ChangeSelectedInventorySlotRequest request, EntityRef character, SelectedInventorySlotComponent selectedInventorySlotComponent) { if (request.getSlot() >= 0 && request.getSlot() < 10 && request.getSlot() != selectedInventorySlotComponent.slot) { EntityRef newItem = InventoryUtils.getItemAt(character, request.getSlot()); selectedInventorySlotComponent.slot = request.getSlot(); character.saveComponent(selectedInventorySlotComponent); character.send(new ChangeHeldItemRequest(newItem)); } } @ReceiveEvent(components = {CharacterComponent.class, LocationComponent.class}, netFilter = RegisterMode.AUTHORITY) public void onDropItemRequest(DropItemRequest event, EntityRef character) { //make sure we own the item and it exists if (!event.getItem().exists() || !networkSystem.getOwnerEntity(event.getItem()).equals(networkSystem.getOwnerEntity(character))) { return; } int count = event.getCount(); // remove 'count' items from the stack EntityRef pickupItem = event.getItem(); EntityRef owner = pickupItem.getOwner(); if (owner.hasComponent(InventoryComponent.class)) { final EntityRef removedItem = inventoryManager.removeItem(owner, EntityRef.NULL, pickupItem, false, count); if (removedItem != null) { pickupItem = removedItem; } } pickupItem.send(new DropItemEvent(event.getNewPosition())); if (pickupItem.hasComponent(PickupComponent.class)) { PickupComponent pickupComponent = pickupItem.getComponent(PickupComponent.class); pickupComponent.timeDropped = time.getGameTimeInMs(); pickupItem.saveComponent(pickupComponent); } pickupItem.send(new ImpulseEvent(event.getImpulse())); } @ReceiveEvent(components = {CharacterComponent.class}, netFilter = RegisterMode.CLIENT) public void onNextItem(ToolbarNextButton event, EntityRef entity, SelectedInventorySlotComponent selectedInventorySlotComponent) { int nextSlot = (selectedInventorySlotComponent.slot + 1) % 10; localPlayer.getCharacterEntity().send(new ChangeSelectedInventorySlotRequest(nextSlot)); event.consume(); } @ReceiveEvent(components = {CharacterComponent.class}, netFilter = RegisterMode.CLIENT) public void onPrevItem(ToolbarPrevButton event, EntityRef entity, SelectedInventorySlotComponent selectedInventorySlotComponent) { int prevSlot = (selectedInventorySlotComponent.slot + 9) % 10; localPlayer.getCharacterEntity().send(new ChangeSelectedInventorySlotRequest(prevSlot)); event.consume(); } @ReceiveEvent(components = {CharacterComponent.class}, netFilter = RegisterMode.CLIENT) public void onSlotButton(ToolbarSlotButton event, EntityRef entity) { localPlayer.getCharacterEntity().send(new ChangeSelectedInventorySlotRequest(event.getSlot())); event.consume(); } @ReceiveEvent(components = {CharacterComponent.class, InventoryComponent.class}, netFilter = RegisterMode.CLIENT) public void onDropItemRequest(DropItemButton event, EntityRef entity) { CharacterHeldItemComponent characterHeldItemComponent = entity.getComponent(CharacterHeldItemComponent.class); EntityRef selectedItemEntity = characterHeldItemComponent.selectedItem; if (selectedItemEntity.equals(EntityRef.NULL)) { return; } //if this is our first time throwing, set the timer to something sensible, we can return since // this is a repeating event. if (event.isDown() && lastTimeThrowInteraction == 0) { lastTimeThrowInteraction = time.getGameTimeInMs(); return; } //resize the crosshair InventoryHud toolbar = nuiManager.getHUD().getHUDElement("core:InventoryHud", InventoryHud.class); if (toolbar != null) { toolbar.setChargeAmount(getDropPower()); } float dropPower = getDropPower(); //handle when we finally let go if (!event.isDown()) { // Compute new position dropPower *= 150f; Vector3f position = localPlayer.getViewPosition(); Vector3f direction = localPlayer.getViewDirection(); Vector3f maxAllowedDistanceInDirection = direction.mul(1.5f); HitResult hitResult = physics.rayTrace(position, direction, 1.5f, StandardCollisionGroup.CHARACTER, StandardCollisionGroup.WORLD); if (hitResult.isHit()) { Vector3f possibleNewPosition = hitResult.getHitPoint(); maxAllowedDistanceInDirection = possibleNewPosition.sub(position); } Vector3f newPosition = position; newPosition.add(maxAllowedDistanceInDirection.mul(0.9f)); //send DropItemRequest Vector3f impulseVector = new Vector3f(direction); impulseVector.scale(dropPower); entity.send(new DropItemRequest(selectedItemEntity, entity, impulseVector, newPosition)); characterHeldItemComponent.lastItemUsedTime = time.getGameTimeInMs(); entity.saveComponent(characterHeldItemComponent); resetDropMark(); } event.consume(); } public void resetDropMark() { InventoryHud toolbar = nuiManager.getHUD().getHUDElement("core:InventoryHud", InventoryHud.class); if (toolbar != null) { toolbar.setChargeAmount(0); } lastTimeThrowInteraction = 0; } private float getDropPower() { if (lastTimeThrowInteraction == 0) { return 0; } float dropPower = (time.getGameTimeInMs() - lastTimeThrowInteraction) / 1200f; return Math.min(1.0f, dropPower); } @ReceiveEvent(netFilter = RegisterMode.AUTHORITY) public void onGiveItemToEntity(GiveItemEvent event, EntityRef entity) { if (event.getTargetEntity().hasComponent(InventoryComponent.class)) { if (inventoryManager.giveItem(event.getTargetEntity(), entity, entity)) { event.getTargetEntity().send(new PlaySoundForOwnerEvent(Assets.getSound("engine:Loot").get(), 1.0f)); event.setHandled(true); } } } @ReceiveEvent(netFilter = RegisterMode.CLIENT) public void onPlayerDeath(DeathEvent event, EntityRef entity) { resetDropMark(); } }