/* * Copyright 2015 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.players; import org.terasology.entitySystem.entity.EntityBuilder; import org.terasology.entitySystem.entity.EntityManager; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.entitySystem.entity.lifecycleEvents.OnActivatedComponent; import org.terasology.entitySystem.entity.lifecycleEvents.OnChangedComponent; 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.logic.characters.GazeAuthoritySystem; import org.terasology.logic.console.commandSystem.annotations.Command; import org.terasology.logic.location.Location; import org.terasology.logic.location.LocationComponent; import org.terasology.logic.permission.PermissionManager; import org.terasology.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.logic.players.event.ResetCameraEvent; import org.terasology.math.geom.Quat4f; import org.terasology.math.geom.Vector3f; import org.terasology.network.ClientComponent; import org.terasology.registry.In; /** * This is a system that creates and maintains a client side entity for the camera. * <p> * Entity Creation Flow: * - ClientComponent for the local player added or changed * - If Camera does not exist, create "engine:camera" entity * - Save the ClientComponent with the new camera attached * <p> * Reset Camera Event Flow: * - ResetCameraEvent * - Camera entity on the ClientComponent is destroyed and set to EntityRef.NULL * - ClientComponent is saved * <p> * Auto Mount Camera Flow: * - ClientComponent or AutoMountCameraComponent for the local player added or changed * - The local player's camera entity (created from above steps) is location linked to the Gaze Entity */ @RegisterSystem(RegisterMode.CLIENT) public class CameraClientSystem extends BaseComponentSystem { @In LocalPlayer localPlayer; @In EntityManager entityManager; @ReceiveEvent public void ensureCameraEntityCreatedOnChangedClientComponent(OnChangedComponent event, EntityRef client, ClientComponent clientComponent) { if (localPlayer.getClientEntity().equals(client)) { ensureCameraEntityCreated(); } } @ReceiveEvent public void ensureCameraEntityCreatedOnActivateClientComponent(OnActivatedComponent event, EntityRef client, ClientComponent clientComponent) { if (localPlayer.getClientEntity().equals(client)) { ensureCameraEntityCreated(); } } private void ensureCameraEntityCreated() { if (!localPlayer.getCameraEntity().exists()) { ClientComponent clientComponent = localPlayer.getClientEntity().getComponent(ClientComponent.class); // create the camera from the prefab EntityBuilder builder = entityManager.newBuilder("engine:camera"); builder.setPersistent(false); clientComponent.camera = builder.build(); localPlayer.getClientEntity().saveComponent(clientComponent); } } @ReceiveEvent public void resetCameraOnCharacterSpawn(OnPlayerSpawnedEvent event, EntityRef character) { if (localPlayer.getCharacterEntity().equals(character)) { resetCamera(); } } @Command(shortDescription = "Reset the camera position", requiredPermission = PermissionManager.NO_PERMISSION) public void resetCamera() { localPlayer.getClientEntity().send(new ResetCameraEvent()); } @ReceiveEvent public void resetCameraForClient(ResetCameraEvent resetCameraEvent, EntityRef entityRef, ClientComponent clientComponent) { if (localPlayer.getClientEntity().equals(entityRef)) { clientComponent.camera.destroy(); clientComponent.camera = EntityRef.NULL; // this will trigger a ClientComponent change which will in turn trigger recreation of the camera entity localPlayer.getClientEntity().saveComponent(clientComponent); } } @ReceiveEvent public void mountCameraOnActivate(OnActivatedComponent event, EntityRef entityRef, AutoMountCameraComponent autoMountCameraComponent, ClientComponent clientComponent) { if (localPlayer.getClientEntity().equals(entityRef) && clientComponent.camera.exists()) { mountCamera(); } } @ReceiveEvent public void mountCameraOnChange(OnChangedComponent event, EntityRef entityRef, AutoMountCameraComponent autoMountCameraComponent, ClientComponent clientComponent) { if (localPlayer.getClientEntity().equals(entityRef) && clientComponent.camera.exists()) { mountCamera(); } } private void mountCamera() { ClientComponent clientComponent = localPlayer.getClientEntity().getComponent(ClientComponent.class); EntityRef targetEntityForCamera = GazeAuthoritySystem.getGazeEntityForCharacter(clientComponent.character); LocationComponent cameraLocation = clientComponent.camera.getComponent(LocationComponent.class); // if the camera already has a location, use that as the relative position of the camera if (cameraLocation != null) { Location.attachChild(targetEntityForCamera, clientComponent.camera, cameraLocation.getLocalPosition(), new Quat4f(Quat4f.IDENTITY)); } else { Location.attachChild(targetEntityForCamera, clientComponent.camera, Vector3f.zero(), new Quat4f(Quat4f.IDENTITY)); } } }