public static EntityTemplate Drone(AxialCoordinate position, string playerId)
        {
            // Calculate the world position based off the grid position.
            var worldPosition = HexUtils.GridToWorld(position);

            var entityTemplate = new EntityTemplate();

            entityTemplate.AddComponent(
                new Drone.Snapshot {
                Owner = playerId, CommandQueue = new List <Command>()
            },
                WorkerUtils.UnityGameLogic);

            entityTemplate.AddComponent(
                new Position.Snapshot(new Coordinates
            {
                X = worldPosition.x,
                Z = worldPosition.y
            }),
                WorkerUtils.UnityGameLogic);

            entityTemplate.AddComponent(
                new PlayerInventory.Snapshot(0, 0),
                WorkerUtils.UnityGameLogic);

            entityTemplate.AddComponent(new Metadata.Snapshot("Drone"), WorkerUtils.UnityGameLogic);
            entityTemplate.AddComponent(new Persistence.Snapshot(), WorkerUtils.UnityGameLogic);
            entityTemplate.SetReadAccess(WorkerUtils.AllWorkers);
            entityTemplate.SetComponentWriteAccess(EntityAcl.ComponentId, WorkerUtils.UnityGameLogic);

            return(entityTemplate);
        }
        private void Update()
        {
            // TODO: Use a better source of pointer input than Input.mousePosition.
            var viewportPosition = Input.mousePosition;

            viewportPosition.z = _camera.transform.position.y;
            var worldPosition = _camera.ScreenToWorldPoint(viewportPosition);

            // Snap the cursor display to the cell the mouse is over.
            _cursorGridPosition        = HexUtils.WorldToGrid(new Vector2(worldPosition.x, worldPosition.z));
            worldPosition              = HexUtils.GridToWorld(_cursorGridPosition);
            _cursor.transform.position = new Vector3(worldPosition.x, 0f, worldPosition.y);

            // TODO: Use a better source of input than Input. It would be nice to
            // use the EventSystem, but it's not clear how use it when clicking on
            // empty space.
            if (Input.GetMouseButtonDown(0) && _selectedDrone != null)
            {
                var command = new MoveToPosition
                {
                    Target = _cursorGridPosition,
                };

                AddCommandToQueue(CommandType.MoveToPosition, JsonUtility.ToJson(command), 5);
            }
        }
        private static Snapshot CreateSnapshot(Arguments arguments)
        {
            var snapshot = new Snapshot();

            AddPlayerSpawner(snapshot);
            AddSpawnController(snapshot);

            var halfWidth = arguments.WorldDimensions.x / 2f;
            var halfHeight = arguments.WorldDimensions.y / 2f;

            for (var i = 0; i < arguments.NumResourceNodes; i += 1)
            {
                // Generate random grid position that is within the real world bounds.
                //
                // TODO: Use a better algorithm for picking random grid coordinates.
                // We're currently picking random spots in a skewed rectangle, and
                // it would better to more evenly distrubte the nodes around the
                // center of the map.
                AxialCoordinate gridPos;
                Vector2 worldPos;
                do
                {
                    gridPos = new AxialCoordinate(
                        (int)Random.Range(-halfWidth, halfWidth),
                        (int)Random.Range(-halfHeight, halfHeight));
                    worldPos = HexUtils.GridToWorld(gridPos);
                } while (Mathf.Abs(worldPos.x) >= halfWidth || Mathf.Abs(worldPos.y) >= halfHeight);

                var type = Random.value < 0.5f ? ResourceType.Coal : ResourceType.Copper;

                AddResourceNode(snapshot, gridPos, type);
            }

            return snapshot;
        }
        private static void AddResourceNode(Snapshot snapshot, AxialCoordinate position, ResourceType type)
        {
            var template = new EntityTemplate();

            var worldPos = HexUtils.GridToWorld(position);
            template.AddComponent(new Position.Snapshot(new Coordinates(worldPos.x, 0, worldPos.y)), WorkerUtils.UnityGameLogic);
            template.AddComponent(
                new Metadata.Snapshot { EntityType = "ResourceNode" },
                WorkerUtils.UnityGameLogic);
            template.AddComponent(new Persistence.Snapshot(), WorkerUtils.UnityGameLogic);
            template.AddComponent(new ResourceNode.Snapshot(type, 100), WorkerUtils.UnityGameLogic);
            template.AddComponent(new GridPosition.Snapshot(position.ToGridCoordinate()), WorkerUtils.UnityGameLogic);

            template.SetReadAccess(WorkerUtils.AllWorkers);
            template.SetComponentWriteAccess(EntityAcl.ComponentId, WorkerUtils.UnityGameLogic);

            snapshot.AddEntity(template);
        }
Example #5
0
        private void Update()
        {
            // If the drone has a command it's actively working on, perform any work
            // related to that command. Otherwise, if there's a command in the queue,
            // make it the active command.
            if (_activeCommand.HasValue)
            {
                var command = _activeCommand.Value;
                switch (command.Type)
                {
                case CommandType.MoveToPosition:
                    var moveToPosition = JsonUtility.FromJson <MoveToPosition>(command.Data);

                    // Determine offset to the target position.
                    var targetWorldPos = HexUtils.GridToWorld(moveToPosition.Target);
                    var offset         = targetWorldPos - _position;

                    // Determine the distance to the target and the direction.
                    var distance  = offset.magnitude;
                    var direction = offset.normalized;

                    // If we're within a frame's distance from the target, snap to the
                    // target and end movement. Otherwise, move towards the target.
                    var maxMovement = Time.deltaTime * MOVE_SPEED;
                    if (distance > maxMovement)
                    {
                        // Update the world position of the drone.
                        _position = _position + maxMovement * direction;
                    }
                    else
                    {
                        // Snap to the target position and clear the active command.
                        _position      = targetWorldPos;
                        _activeCommand = null;
                    }

                    break;

                case CommandType.HarvestResourceNode:
                    var harvestResourceNode = JsonUtility.FromJson <HarvestResourceNode>(command.Data);

                    if (!resourceCollectionTimer.HasValue)
                    {
                        resourceCollectionTimer = 0;
                    }
                    else if (resourceCollectionTimer.Value < 1f)
                    {
                        resourceCollectionTimer += Time.deltaTime;
                    }
                    else
                    {
                        resourceCollectionTimer = null;

                        Entity targetEntity;
                        if (_linkedEntity.Worker.TryGetEntity(new EntityId(harvestResourceNode.Target), out targetEntity))
                        {
                            var entityManager         = _linkedEntity.World.EntityManager;
                            var resourceNodeComponent = entityManager.GetComponentData <ResourceNode.Component>(targetEntity);

                            uint harvestedQuantity = System.Math.Min(1, resourceNodeComponent.Quantity);
                            resourceNodeComponent.Quantity -= harvestedQuantity;
                            entityManager.SetComponentData <ResourceNode.Component>(targetEntity, resourceNodeComponent);

                            switch (resourceNodeComponent.Type)
                            {
                            case ResourceType.Coal:
                                _inventory.Coal += harvestedQuantity;
                                break;

                            case ResourceType.Copper:
                                _inventory.Copper += harvestedQuantity;
                                break;
                            }

                            if (resourceNodeComponent.Quantity <= 0)
                            {
                                // Clear the active command.
                                _activeCommand = null;
                            }
                        }
                        else
                        {
                            Debug.Log($"Target resource node {harvestResourceNode.Target} doesn't exist, cancelling command");

                            // Clear the active command.
                            _activeCommand = null;
                        }
                    }

                    break;

                default:
                    Debug.LogWarning($"Unable to perform unknown command {command.Type}, discarding active command");

                    // Discard the active command,
                    _activeCommand = null;

                    break;
                }
            }
            else if (_commandQueue.Count > 0)
            {
                _activeCommand = _commandQueue[0];
                _commandQueue.RemoveAt(0);

                Debug.Log($"Making command {_activeCommand} active for drone {_linkedEntity.EntityId}");
            }

            var worldPosition = new Vector3(_position.x, 0f, _position.y);

            transform.position = worldPosition + _linkedEntity.Worker.Origin;

            // Periodically send updates to components that are rate-limited by time.
            _timeToNextUpdate -= Time.deltaTime;
            if (_timeToNextUpdate < 0f || _droneWriter.Authority == Authority.AuthorityLossImminent)
            {
                // Check for updates to the position component.
                if (_timeToNextUpdate < 0f && !worldPosition.Equals(_positionWriter.Data.Coords))
                {
                    _positionWriter.SendUpdate(new Position.Update
                    {
                        Coords = new Coordinates(worldPosition.x, worldPosition.y, worldPosition.z),
                    });
                }

                // Check for updates to the inventory.
                if (_timeToNextUpdate < 0f && _inventory.IsDataDirty())
                {
                    _inventoryWriter.SendUpdate(new PlayerInventory.Update
                    {
                        Coal   = _inventory.Coal,
                        Copper = _inventory.Copper,
                    });
                    _inventory.MarkDataClean();
                }

                // Track drone updates.
                var droneUpdate = new Drone.Update();
                var droneDirty  = false;

                // TODO: Use regular == operator once SpatialOS generates the necessary code.
                if (!_activeCommand.Equals(_droneWriter.Data.ActiveCommand))
                {
                    droneUpdate.ActiveCommand = _activeCommand;
                    droneDirty = true;
                }

                if (!_commandQueue.SequenceEqual(_droneWriter.Data.CommandQueue))
                {
                    droneUpdate.CommandQueue = _commandQueue;
                    droneDirty = true;
                }

                if (droneDirty)
                {
                    _droneWriter.SendUpdate(droneUpdate);
                }

                // Reset the time to the next update.
                _timeToNextUpdate = UPDATE_INTERVAL;
            }
        }