Exemple #1
0
 public AddCuboid(Position position1, Position position2, Block.BlockType blockType)
     : this()
 {
     Position1 = position1;
     Position2 = position2;
     BlockType = blockType;
 }
Exemple #2
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var bytes = ReadStream(DataLength);
                    Position = new Position(bytes, 0);
                    BlockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Position.SIZE);
                }
            }

            WorldData.PlaceBlock(Position, BlockType);

            if (Config.IsServer)
            {
                //bm: this has to wait until the server can manage who's in creative mode
                //if (ConnectedPlayer.Inventory[(int)BlockType] <= 0) return;
                //ConnectedPlayer.Inventory[(int)BlockType] -= 1;

                foreach (var player in Server.Controller.Players.Values)
                {
                    new AddBlock(ref Position, BlockType) { ConnectedPlayer = player }.Send();
                }
            }
        }
 internal BlockCursorHost()
 {
     Position = new Position();
     _positionAdd = new Position();
     _blockCursorUpdateTimer = new Stopwatch();
     _blockCursorUpdateTimer.Start();
 }
Exemple #4
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var bytes = ReadStream(DataLength);
                    Position = new Position(bytes, 0);
                }
            }

            var existingBlock = Position.GetBlock(); //store the existing block before we overwrite it
            WorldData.PlaceBlock(Position, Block.BlockType.Air);

            //if destroying a block, create an item
            BlockItem newBlockItem = null;
            if ((Config.IsSinglePlayer && !Config.CreativeMode) || (Config.IsServer && !ConnectedPlayer.IsCreative))
            {
                if (!existingBlock.IsTransparent)
                {
                    var temp = Position.ToCoords();
                    newBlockItem = new BlockItem(ref temp, existingBlock.Type);
                }
            }

            if (Config.IsServer)
            {
                foreach (var player in Server.Controller.Players.Values)
                {
                    new RemoveBlock(ref Position) { ConnectedPlayer = player }.Send();
                    if (newBlockItem != null) new AddBlockItem(ref newBlockItem.Coords, ref newBlockItem.Velocity, newBlockItem.BlockType, newBlockItem.Id) {ConnectedPlayer = player}.Send();
                }
            }
        }
 public AddStructure(Position position, StructureType structureType, Facing frontFace)
     : this()
 {
     Position = position;
     StructureType = structureType;
     FrontFace = frontFace;
 }
Exemple #6
0
 public AddBlock(ref Position position, Block.BlockType blockType)
     : this()
 {
     if (blockType == Block.BlockType.Air) throw new Exception("You can't place air, use RemoveBlock");
     Position = position;
     BlockType = blockType;
 }
Exemple #7
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var bytes = ReadStream(DataLength);
                    Position1 = new Position(bytes, 0);
                    Position2 = new Position(bytes, Position.SIZE);
                    BlockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Position.SIZE * 2);
                }
            }

            WorldData.PlaceCuboid(Position1, Position2, BlockType);

            if (Config.IsServer)
            {
                foreach (var player in Server.Controller.Players.Values)
                {
                    new AddCuboid(Position1, Position2, BlockType) { ConnectedPlayer = player }.Send();
                }
            }
            else
            {
                //play the sound relative to the closer of the 2 diagonal corners (accurate enough for cuboids, otherwise we would have to check around the entire perimeter)
                var playerPosition = Game.Player.Coords.ToPosition();
                if (Position1.GetDistanceExact(ref playerPosition) < Position2.GetDistanceExact(ref playerPosition))
                {
                    Sounds.Audio.PlaySound(Sounds.SoundType.AddBlock, ref Position1);
                }
                else
                {
                    Sounds.Audio.PlaySound(Sounds.SoundType.AddBlock, ref Position2);
                }
            }
        }
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var blockCount = BitConverter.ToInt32(ReadStream(sizeof(int)), 0);

                    for (var i = 0; i < blockCount; i++)
                    {
                        var bytes = ReadStream(Position.SIZE + sizeof(ushort));
                        var position = new Position(bytes, 0);
                        var blockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Position.SIZE);
                        Blocks.Add(new AddBlock(ref position, blockType));
                    }
                }
            }

            Settings.ChunkUpdatesDisabled = true;
            foreach (var addBlock in Blocks) WorldData.PlaceBlock(addBlock.Position, addBlock.BlockType);
            Settings.ChunkUpdatesDisabled = false;

            if (Config.IsServer)
            {
                //bm: this has to wait until the server can manage who's in creative mode
                //if (ConnectedPlayer.Inventory[(int)BlockType] <= 0) return;
                //ConnectedPlayer.Inventory[(int)BlockType] -= 1;

                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti { ConnectedPlayer = player };
                    addBlockMulti.Blocks.AddRange(Blocks);
                    addBlockMulti.Send();
                }
            }
        }
Exemple #9
0
 private static void _fastToolTimer_Elapsed(object sender, ElapsedEventArgs e)
 {
     if (CurrentTool == ToolType.FastDestroy && !BlockCursorHost.Position.Equals(_prevFastPositionSent))
     {
         _prevFastPositionSent = BlockCursorHost.Position;
         Game.InputHost.AddOrRemoveBlock(Block.BlockType.Air);
     }
     else if (CurrentTool == ToolType.FastBuild && !BlockCursorHost.PositionAdd.Equals(_prevFastPositionSent))
     {
         _prevFastPositionSent = BlockCursorHost.PositionAdd;
         var buttonToolBlockType = _toolButtons[(int)ToolType.ToolBlockType];
         if (buttonToolBlockType.BlockType > 0) //only add a block if a non air block type is selected
         {
             Game.InputHost.AddOrRemoveBlock(buttonToolBlockType.BlockType);
         }
         else if (buttonToolBlockType.LightSourceType.HasValue)
         {
             var position = BlockCursorHost.PositionAdd;
             if (WorldData.IsValidStaticItemPosition(position))
             {
                 var lightSource = new LightSource(ref position, buttonToolBlockType.LightSourceType.Value, BlockCursorHost.SelectedFace.ToOpposite());
                 new AddStaticItem(lightSource).Send();
             }
         }
     }
 }
Exemple #10
0
        public static void LeftMouseDown(int x, int y)
        {
            foreach (var button in _actionButtons)
            {
                if (button.ContainsCoords(x, y)) //clicked action bar button
                {
                    if (BlockPickerGridVisible) //picker already open
                    {
                        _currentActionButton.Hightlight = false; //unhighlight current action button
                        if(button == _currentActionButton)
                        {
                            //reclicked currently selected button, close picker
                            BlockPickerGridVisible = false;
                            return;
                        }
                    }
                    BlockPickerGridVisible = true; //open block picker
                    button.Hightlight = true; //highlight selected button
                    _currentActionButton = button; //store the button that was clicked so the block type can be set once picked
                    _cancelMouseUp = true; //dont run mouseup for this click to prevent accidentally using current tool "through" the button that was clicked
                    return;
                }
            }

            if (BlockPickerGridVisible)
            {
                foreach (var button in _blockPickerGridButtons)
                {
                    if (button.ContainsCoords(x, y)) //block picked from grid
                    {
                        _currentActionButton.Texture = button.Texture;
                        _currentActionButton.BlockType = button.BlockType;
                        _currentActionButton.LightSourceType = button.LightSourceType;

                        if (_currentActionButton.ToolType == ToolType.ToolBlockType)
                        {
                            //selected block type for right bar block type; re-select previous tool
                            _currentToolButton = _previousToolButton;
                            _currentToolButton.Hightlight = true;
                            _previousToolButton = null;
                        }

                        BlockPickerGridVisible = false;
                        _cancelMouseUp = true;
                        _currentActionButton = null;
                        return;
                    }
                }
            }

            if (Config.CreativeMode)
            {
                foreach (var button in _toolButtons)
                {
                    if (button.ContainsCoords(x, y)) //tool button clicked
                    {
                        SelectTool(button.ToolType);
                        _cancelMouseUp = true;

                        if (button.ToolType == ToolType.ToolBlockType)
                        {
                            BlockPickerGridVisible = !BlockPickerGridVisible; //open/close block picker
                            _currentActionButton = button; //store the ToolBlockType button as the current action button so the block type can be set once picked
                        }
                        else //close the block picker grid in case it was open for the ToolBlockType tool
                        {
                            BlockPickerGridVisible = false;
                        }

                        return;
                    }
                }

                switch (CurrentTool)
                {
                    case ToolType.Cuboid:
                        _leftMouseDownPosition = BlockCursorHost.PositionAdd;
                        break;
                    case ToolType.FastBuild:
                    case ToolType.FastDestroy:
                        //perform one action as soon as clicked then start timer to continue if mouse held down
                        _fastToolTimer_Elapsed(null, null);
                        FastToolTimer.Start();
                        break;
                }
            }
        }
Exemple #11
0
        public static void LeftMouseUp(int x, int y)
        {
            if(_cancelMouseUp)
            {
                _cancelMouseUp = false;
                return;
            }

            if (Config.CreativeMode)
            {
                FastToolTimer.Stop();
                _prevFastPositionSent = new Position(); //reset the prev position sent, so the fast build or destroy tools can subsequently be used on the same position again

                switch (CurrentTool)
                {
                    case ToolType.Cuboid:
                        if (BlockCursorHost.PositionAdd.IsValidBlockLocation && _leftMouseDownPosition.IsValidBlockLocation && BlockCursorHost.PositionAdd.GetDistanceExact(ref _leftMouseDownPosition) > 3)
                        {
                            var leftMouseUpPosition = _toolButtons[(int)ToolType.ToolBlockType].BlockType == Block.BlockType.Air ? BlockCursorHost.Position : BlockCursorHost.PositionAdd;
                            if (!leftMouseUpPosition.IsValidBlockLocation) return;
                            new AddCuboid(_leftMouseDownPosition, leftMouseUpPosition, _toolButtons[(int)ToolType.ToolBlockType].BlockType).Send();
                        }
                        return;
                    case ToolType.Tree:
                        new AddStructure(BlockCursorHost.PositionAdd, StructureType.Tree, Game.Player.Coords.DirectionFacing().ToOpposite()).Send();
                        return;
                    case ToolType.Tower:
                        new AddStructure(BlockCursorHost.PositionAdd, StructureType.Tower, Game.Player.Coords.DirectionFacing().ToOpposite()).Send();
                        return;
                    case ToolType.SmallKeep:
                        new AddStructure(BlockCursorHost.PositionAdd, StructureType.SmallKeep, Game.Player.Coords.DirectionFacing().ToOpposite()).Send();
                        return;
                    case ToolType.LargeKeep:
                        new AddStructure(BlockCursorHost.PositionAdd, StructureType.LargeKeep, Game.Player.Coords.DirectionFacing().ToOpposite()).Send();
                        return;
                }
            }
        }
        /// <summary>Send message to the server to add or remove a block. Block will be removed if the block type is Air.</summary>
        /// <param name="position">position to add the block</param>
        /// <param name="blockType">block type to add</param>
        public static void SendAddOrRemoveBlock(Position position, Block.BlockType blockType)
        {
            if (!position.IsValidBlockLocation) return;
            if (blockType == Block.BlockType.Air) //remove block
            {
                if (position.Y == 0) { Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, "Cannot remove a block at the base of the world. Block cancelled.")); return; }
                new RemoveBlock(ref position).Send();
            }
            else //add block
            {
                var head = Game.Player.CoordsHead;
                if (!Config.CreativeMode && Game.Player.Inventory[(int)blockType] <= 0) { Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, string.Format("No {0} in inventory.", blockType))); return; }
                if (Block.IsBlockTypeSolid(blockType) && (position.IsOnBlock(ref Game.Player.Coords) || position.IsOnBlock(ref head))) { Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, "Attempted to build solid block on self. Not smart. Block cancelled.")); return; }

                foreach (var player in Players.Values)
                {
                    head = player.CoordsHead;
                    if (!Block.IsBlockTypeSolid(blockType) || (!position.IsOnBlock(ref player.Coords) && !position.IsOnBlock(ref head))) continue;
                    Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, "Attempted to build solid block on other player. Not nice. Block cancelled.")); return;
                }
                new AddBlock(ref position, blockType).Send();
                if (!Config.CreativeMode) Game.Player.Inventory[(int)blockType]--;
            }
        }
 /// <summary>Use this constructor to create brand new items that will have the Coords shifted accordingly during creation.</summary>
 protected GameItemStatic(ref Position position, Face attachedToFace)
 {
     Coords = position.ToCoords();
     AttachedToFace = attachedToFace;
 }
Exemple #14
0
 public RemoveBlock(ref Position position)
     : this()
 {
     Position = position;
 }
Exemple #15
0
 internal Block this[Position position]
 {
     get { return new Block(Array[position.Y, position.X % Chunk.CHUNK_SIZE, position.Z % Chunk.CHUNK_SIZE]); }
     set { Array[position.Y, position.X % Chunk.CHUNK_SIZE, position.Z % Chunk.CHUNK_SIZE] = value.BlockData; }
 }
Exemple #16
0
 /// <summary>Plays a sound with a volume relative to how far from the listener it is.</summary>
 /// <param name="sound">sound type to play</param>
 /// <param name="sourcePosition">source position of the sound</param>
 /// <param name="maxDistance">max distance the sound can be heard</param>
 internal static void PlaySound(SoundType sound, ref Position sourcePosition, byte maxDistance = 25)
 {
     if (!Config.SoundEnabled) return;
     float gain = (maxDistance - Game.Player.Coords.ToPosition().GetDistanceExact(ref sourcePosition)) / maxDistance;
     Play(sound, gain);
 }
        public static void BuildCastle(Position center, int radius, int height, Block.BlockType blockType, Facing frontFace)
        {
            //make sure diagonal positions are valid
            if (!WorldData.IsValidBlockLocation(center.X - radius, center.Y, center.Z - radius)) return;
            if (!WorldData.IsValidBlockLocation(center.X + radius, center.Y, center.Z + radius)) return;

            //clear area with air
            WorldData.PlaceCuboid(new Position(center.X - radius, center.Y, center.Z - radius), new Position(center.X + radius, center.Y + height - 1, center.Z + radius), Block.BlockType.Air, true);
            //front wall
            WorldData.PlaceCuboid(new Position(center.X - radius, center.Y, center.Z + radius), new Position(center.X + radius, center.Y + height - 1, center.Z + radius), blockType, true);
            //back wall
            WorldData.PlaceCuboid(new Position(center.X - radius, center.Y, center.Z - radius), new Position(center.X + radius, center.Y + height - 1, center.Z - radius), blockType, true);
            //left wall
            WorldData.PlaceCuboid(new Position(center.X - radius, center.Y, center.Z - radius), new Position(center.X - radius, center.Y + height - 1, center.Z + radius), blockType, true);
            //right wall
            WorldData.PlaceCuboid(new Position(center.X + radius, center.Y, center.Z - radius), new Position(center.X + radius, center.Y + height - 1, center.Z + radius), blockType, true);

            //front and back tops
            for (var x = center.X - radius; x <= center.X + radius; x += 2)
            {
                WorldData.PlaceBlock(new Position(x, center.Y + height, center.Z + radius), blockType, true);
                WorldData.PlaceBlock(new Position(x, center.Y + height, center.Z - radius), blockType, true);
            }
            //side tops
            for (var z = center.Z - radius; z <= center.Z + radius; z += 2)
            {
                WorldData.PlaceBlock(new Position(center.X + radius, center.Y + height, z), blockType, true);
                WorldData.PlaceBlock(new Position(center.X - radius, center.Y + height, z), blockType, true);
            }

            //top wood floor
            WorldData.PlaceCuboid(new Position(center.X - radius + 1, center.Y + height - 2, center.Z - radius + 1), new Position(center.X + radius - 1, center.Y + height - 2, center.Z + radius - 1), Block.BlockType.WoodTile2, true);

            //door/lights
            Position doorBottom = center;
            Position outsideLight1;
            Position outsideLight2;
            switch (frontFace)
            {
                case Facing.North:
                    doorBottom.Z -= radius;
                    outsideLight1 = doorBottom;
                    outsideLight1.Z--;
                    outsideLight2 = outsideLight1;
                    outsideLight1.X--;
                    outsideLight2.X++;
                    break;
                case Facing.East:
                    doorBottom.X += radius;
                    outsideLight1 = doorBottom;
                    outsideLight1.X++;
                    outsideLight2 = outsideLight1;
                    outsideLight1.Z--;
                    outsideLight2.Z++;
                    break;
                case Facing.South:
                    doorBottom.Z += radius;
                    outsideLight1 = doorBottom;
                    outsideLight1.Z++;
                    outsideLight2 = outsideLight1;
                    outsideLight1.X--;
                    outsideLight2.X++;
                    break;
                default:
                    doorBottom.X -= radius;
                    outsideLight1 = doorBottom;
                    outsideLight1.X--;
                    outsideLight2 = outsideLight1;
                    outsideLight1.Z--;
                    outsideLight2.Z++;
                    break;
            }
            outsideLight1.Y += 2;
            outsideLight2.Y += 2;
            WorldData.PlaceCuboid(doorBottom, new Position(doorBottom.X, doorBottom.Y + 1, doorBottom.Z), Block.BlockType.Air, true); //clear space for door

            //add lights
            //todo: downside to this is its causing extra chunk queues and lightbox rebuilds, only for structure builds so not a huge deal for now
            //-clients are independently adding the lightsources, so id's might not match with server, for now theres no reason requiring them to match
            //-another quirk is if the location ends up not being valid, the client will get an error msg and not know why if this came from the server
            //-eventually may want a way for the AddStructure packet to include them, structures might get built containing many static items...
            if (WorldData.IsValidStaticItemPosition(outsideLight1))
            {
                var ls1 = new LightSource(ref outsideLight1, LightSourceType.Lantern, frontFace.ToFace().ToOpposite());
                new LightSource(ref ls1.Coords, ls1.Type, ls1.AttachedToFace, ls1.Id);
            }
            if (WorldData.IsValidStaticItemPosition(outsideLight2))
            {
                var ls2 = new LightSource(ref outsideLight2, LightSourceType.Lantern, frontFace.ToFace().ToOpposite());
                new LightSource(ref ls2.Coords, ls2.Type, ls2.AttachedToFace, ls2.Id);
            }
        }
Exemple #18
0
 /// <summary>Place multiple blocks in the world of the same type.</summary>
 /// <param name="startPosition">start placing blocks at</param>
 /// <param name="endPosition">stop placing blocks at</param>
 /// <param name="type">type of block to place</param>
 /// <param name="isMultipleCuboidPlacement">Use this when placing multiple cuboids at once so lighting and chunk queueing only happens once.</param>
 internal static void PlaceCuboid(Position startPosition, Position endPosition, Block.BlockType type, bool isMultipleCuboidPlacement = false)
 {
     for (var x = Math.Min(startPosition.X, endPosition.X); x <= Math.Max(startPosition.X, endPosition.X); x++)
     {
         for (var y = Math.Min(startPosition.Y, endPosition.Y); y <= Math.Max(startPosition.Y, endPosition.Y); y++)
         {
             for (var z = Math.Min(startPosition.Z, endPosition.Z); z <= Math.Max(startPosition.Z, endPosition.Z); z++)
             {
                 PlaceBlock(new Position(x, y, z), type, true);
             }
         }
     }
     if (!Config.IsServer && !isMultipleCuboidPlacement) ModifyLightAndQueueChunksForCuboidChange(startPosition, endPosition);
 }
Exemple #19
0
 /// <summary>
 /// Update the applicable light box and queue the chunks that were affected. Skip light update if the changing block is transparent as it would have no effect on light.
 /// Light box update is done in a task which queues the affected chunks only after the light box update is finished.
 /// </summary>
 /// <param name="position">Position the light change is originating at (the block that is changing).</param>
 /// <param name="changeInTransparency">Lighting only needs to be recalculated if we are replacing a solid with a transparent or vice versa</param>
 /// <param name="blockType">New block type being placed. Will be Air for block remove.</param>
 private static void ModifyLightAndQueueChunksForBlockChange(Position position, bool changeInTransparency, Block.BlockType blockType)
 {
     if (!changeInTransparency) //transparency did not change; no effect on lighting
     {
         if (position.IsOnChunkBorder)
         {
             var chunks = new Queue<Chunk>();
             chunks.Enqueue(Chunks[position]); //queue this chunk first
             foreach (Chunk chunk in position.BorderChunks) chunks.Enqueue(chunk); //now queue 1 adjacent chunk as well when on chunk border or 2 adjacent chunks when on chunk corner
             Debug.WriteLine("Growth change on chunk border {0}:{1}; Lighting not affected; Queueing {2} chunks", blockType, position, chunks.Count);
             QueueAffectedChunks(chunks);
         }
         else //not on a chunk border, queue this chunk only
         {
             Debug.WriteLine("Growth change {0}:{1}; Lighting not affected; Queue local chunk only", blockType, position);
             Chunks[position].QueueImmediate();
         }
     }
     else
     {
         Task<Queue<Chunk>>.Factory.StartNew(() => Lighting.UpdateLightBox(ref position, null, true, blockType == Block.BlockType.Air)).ContinueWith(task => QueueAffectedChunks(task.Result));
     }
 }
Exemple #20
0
        /// <summary>Place a single block in the world. This will mark the block as dirty.</summary>
        /// <param name="position">position to place the block at</param>
        /// <param name="type">type of block to place</param>
        /// <param name="isMultipleBlockPlacement">Use this when placing multiple blocks at once so lighting and chunk queueing only happens once.</param>
        internal static void PlaceBlock(Position position, Block.BlockType type, bool isMultipleBlockPlacement = false)
        {
            if (!position.IsValidBlockLocation || position.Y <= 0) return;

            //this was a multiple block placement, prevent placing blocks on yourself and getting stuck; used to be able to place cuboids on yourself and get stuck
            //only check in single player for now because in multiplayer this could allow the blocks on different clients to get out of sync and placements of multiple blocks in multiplayer will be rare
            if (Config.IsSinglePlayer && isMultipleBlockPlacement && (position.IsOnBlock(ref Game.Player.Coords) || position == Game.Player.CoordsHead.ToPosition())) return;

            if (type == Block.BlockType.Air)
            {
                //if destroying a block under water, fill with water instead of air
                if (position.Y + 1 < Chunk.CHUNK_HEIGHT && GetBlock(position.X, position.Y + 1, position.Z).Type == Block.BlockType.Water) type = Block.BlockType.Water;
            }

            var chunk = Chunks[position];
            var block = position.GetBlock();
            var oldType = block.Type;
            block.Type = type; //assign the new type
            var isTransparentBlock = Block.IsBlockTypeTransparent(type);
            var isTransparentOldBlock = Block.IsBlockTypeTransparent(oldType);
            block.BlockData = (ushort)(block.BlockData | 0x8000); //mark the block as dirty for the save file "diff" logic
            chunk.Blocks[position] = block; //insert the new block
            chunk.UpdateHeightMap(ref block, position.X % Chunk.CHUNK_SIZE, position.Y, position.Z % Chunk.CHUNK_SIZE);

            if (!isTransparentBlock || type == Block.BlockType.Water)
            {
                var below = position;
                below.Y--;
                if (below.Y > 0)
                {
                    if (below.GetBlock().Type == Block.BlockType.Grass || below.GetBlock().Type == Block.BlockType.Snow)
                    {
                        PlaceBlock(below, Block.BlockType.Dirt, true); //dont queue with this dirt block change, the source block changing takes care of it, prevents double queueing the chunk and playing sound twice
                    }
                }
            }

            if (!chunk.WaterExpanding) //water is not expanding, check if this change should make it start
            {
                switch (type)
                {
                    case Block.BlockType.Water:
                        chunk.WaterExpanding = true;
                        break;
                    case Block.BlockType.Air:
                        for (var q = 0; q < 5; q++)
                        {
                            Position adjacent;
                            switch (q)
                            {
                                case 0:
                                    adjacent = new Position(position.X + 1, position.Y, position.Z);
                                    break;
                                case 1:
                                    adjacent = new Position(position.X - 1, position.Y, position.Z);
                                    break;
                                case 2:
                                    adjacent = new Position(position.X, position.Y + 1, position.Z);
                                    break;
                                case 3:
                                    adjacent = new Position(position.X, position.Y, position.Z + 1);
                                    break;
                                default:
                                    adjacent = new Position(position.X, position.Y, position.Z - 1);
                                    break;
                            }
                            if (adjacent.IsValidBlockLocation && adjacent.GetBlock().Type == Block.BlockType.Water)
                            {
                                Chunks[adjacent].WaterExpanding = true;
                            }
                        }
                        break;
                }
            }

            //its easier to just set .GrassGrowing on all affected chunks to true, and then let that logic figure it out and turn it off, this way the logic is contained in one spot
            //and also the logic here doesnt need to run every time a block gets placed. ie: someone is building a house, its running through this logic for every block placement;
            //now it can only check once on the grass grow interval and turn it back off
            //gm: an additional optimization, grass could never start growing unless this is an air block and its replacing a non transparent block
            //OR this is a non transparent block filling in a previously transparent block to cause grass to die
            if (!isTransparentBlock || (type == Block.BlockType.Air && !isTransparentOldBlock))
            {
                chunk.GrassGrowing = true;
                if (position.IsOnChunkBorder)
                {
                    foreach (var adjacentChunk in position.BorderChunks) adjacentChunk.GrassGrowing = true;
                }
            }

            //determine if any static game items need to be removed as a result of this block placement
            if (type != Block.BlockType.Air)
            {
                lock (chunk.Clutters) //lock because clutter is stored in a HashSet
                {
                    //if theres clutter on this block then destroy it to place the block (FirstOrDefault returns null if no match is found)
                    var clutterToRemove = chunk.Clutters.FirstOrDefault(clutter => position.IsOnBlock(ref clutter.Coords));
                    if (clutterToRemove != null) chunk.Clutters.Remove(clutterToRemove);
                }

                var lightSourceToRemove = chunk.LightSources.FirstOrDefault(lightSource => position.IsOnBlock(ref lightSource.Value.Coords));
                if (lightSourceToRemove.Value != null)
                {
                    LightSource temp;
                    chunk.LightSources.TryRemove(lightSourceToRemove.Key, out temp);
                }
            }
            else //destroying block
            {
                lock (chunk.Clutters) //lock because clutter is stored in a HashSet
                {
                    //if theres clutter on top of this block then remove it as well (FirstOrDefault returns null if no match is found)
                    var clutterToRemove = chunk.Clutters.FirstOrDefault(clutter => clutter.Coords.Xblock == position.X && clutter.Coords.Yblock == position.Y + 1 && clutter.Coords.Zblock == position.Z); //add one to Y to look on the block above
                    if (clutterToRemove != null) chunk.Clutters.Remove(clutterToRemove);
                }

                //look on ALL 6 adjacent blocks for static items, and those only get destroyed if its on the matching opposite attached to face
                var adjacentPositions = position.AdjacentPositionFaces;
                foreach (var tuple in adjacentPositions)
                {
                    var adjBlock = tuple.Item1.GetBlock();
                    if (adjBlock.Type != Block.BlockType.Air) continue; //position cannot contain an item if the block is not air
                    var adjChunk = tuple.Item2 == Face.Top || tuple.Item2 == Face.Bottom ? chunk : Chunks[tuple.Item1]; //get the chunk in case the adjacent position crosses a chunk boundary
                    var lightSourceToRemove = adjChunk.LightSources.FirstOrDefault(lightSource => tuple.Item1.IsOnBlock(ref lightSource.Value.Coords));
                    if (lightSourceToRemove.Value != null && lightSourceToRemove.Value.AttachedToFace == tuple.Item2.ToOpposite()) //remove the light source
                    {
                        LightSource temp;
                        chunk.LightSources.TryRemove(lightSourceToRemove.Key, out temp);
                    }
                }

                //if theres a dynamic item on top of this block then let it fall
                foreach (var item in chunk.GameItems.Values)
                {
                    if (!item.IsMoving && item.Coords.Xblock == position.X && item.Coords.Yblock == position.Y + 1 && item.Coords.Zblock == position.Z)
                    {
                        item.IsMoving = true;
                    }
                }
            }

            if (!Config.IsServer && !isMultipleBlockPlacement)
            {
                Debug.WriteLineIf(type == Block.BlockType.Ice && oldType == Block.BlockType.Water, "Growth change Water->Ice; Multiple lightbox updates and chunk queues are possible");
                ModifyLightAndQueueChunksForBlockChange(position, isTransparentOldBlock != isTransparentBlock, type);

                //sounds dont play for multi/cuboid placements; they are responsible for their own sounds; prevents sound spam
                switch (type)
                {
                    case Block.BlockType.Air:
                        switch (oldType)
                        {
                            case Block.BlockType.Water: //remove water
                                Sounds.Audio.PlaySound(Sounds.SoundType.JumpOutOfWater, ref position);
                                break;
                            default: //remove another type
                                Sounds.Audio.PlaySound(Sounds.SoundType.RemoveBlock, ref position);
                                break;
                        }
                        break;
                    case Block.BlockType.Water: //place water
                        Sounds.Audio.PlaySound(Sounds.SoundType.JumpOutOfWater, ref position);
                        break;
                    default:
                        //only play the add block sound if the old type was air; the only way blocks can change otherwise are the auto changes in chunks for grass/snow/dirt/etc.
                        if (oldType == Block.BlockType.Air) Sounds.Audio.PlaySound(Sounds.SoundType.AddBlock, ref position);
                        break;
                }
            }
        }
Exemple #21
0
 /// <summary>
 /// Update the applicable light box and queue the chunks that were affected. No optimizations are made for transparent blocks because every block in the cuboid would need to be checked.
 /// Light box update is done in a task which queues the affected chunks only after the light box update is finished.
 /// </summary>
 /// <param name="position1">First corner of the cuboid.</param>
 /// <param name="position2">Diagonal corner of the cuboid.</param>
 internal static void ModifyLightAndQueueChunksForCuboidChange(Position position1, Position position2)
 {
     Task<Queue<Chunk>>.Factory.StartNew(() => Lighting.UpdateLightBox(ref position1, position2, true, false)).ContinueWith(task => QueueAffectedChunks(task.Result));
 }
Exemple #22
0
 //cannot accept position by ref here
 internal static bool IsValidStaticItemPosition(Position position)
 {
     if (!IsValidBlockLocation(position.X, position.Y, position.Z))
     {
         Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, string.Format("Invalid item position.")));
         return false;
     }
     var chunk = Chunks[position];
     if (chunk.LightSources.Any(lightSource => position.IsOnBlock(ref lightSource.Value.Coords)) || chunk.Clutters.Any(clutter => position.IsOnBlock(ref clutter.Coords)))
     {
         Game.UiHost.AddChatMessage(new ChatMessage(ChatMessageType.Error, string.Format("Item already exists on selected block.")));
         return false;
     }
     return true;
 }
Exemple #23
0
        /// <summary>
        /// Update a light box for a change originating at the supplied position. Some logic is used to limit the size of the light box where possible.
        /// Update is performed by blanking out all of the existing light in an inner box area, resetting all light emitting sources in the inner box and then
        /// looping through an outer box that is one larger on each side so that light can be pulled back in from the edges where applicable.
        /// </summary>
        /// <remarks>Further logic could be added for more cases where the size of the light box could be decreased, however this is not a big bottleck now (chunk rebuild time is far more worthwhile to optimize).</remarks>
        /// <param name="position">Position the light change is centered at (the block that is changing).</param>
        /// <param name="positionCuboid">Null when only a single block is changing. End of the cuboid when the change is a cuboid.</param>
        /// <param name="isBlockChange">Is this change for a block add or remove. Any other changes, such as item lights, have no need for full vertical updates.</param>
        /// <param name="isRemove">Is this update for a block removal.</param>
        /// <returns>Queue of affected chunks with the chunk where the change originated queued first if this is a single block change.</returns>
        internal static Queue<Chunk> UpdateLightBox(ref Position position, Position? positionCuboid, bool isBlockChange, bool isRemove)
        {
            const int MAX_LIGHT_SPREAD_DISTANCE = SkyHost.BRIGHTEST_SKYLIGHT_STRENGTH;
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            //use a queue to ensure the chunk where the change originates gets queued first for a single block change
            var chunksAffected = new Queue<Chunk>();

            //inner box depends on if this is a single block or cuboid
            int x1Inner;
            int y1Inner;
            int z1Inner;
            int x2Inner;
            int y2Inner;
            int z2Inner;

            if (!positionCuboid.HasValue) //single block change
            {
                var chunkContainingChange = WorldData.Chunks[position];

                //determine if a full vertical update is required;
                //-not required if the block directly under the change is NOT transparent (this is because sunlight couldnt travel down)
                //-not required if this change is below the heightmap because sunlight stops at the heightmap (sunlight is the only light that can travel > than the max spread distance)
                //-not required if this is NOT a block change, item lights etc. cannot affect sunlight
                //(cuts the light rebuild time by approx 60% in these cases)
                bool fullVerticalRequired = isBlockChange && position.Y > 0 && WorldData.GetBlock(position.X, position.Y - 1, position.Z).IsTransparent && position.Y >= chunkContainingChange.HeightMap[position.X % Chunk.CHUNK_SIZE, position.Z % Chunk.CHUNK_SIZE];

                x1Inner = Math.Max(position.X - MAX_LIGHT_SPREAD_DISTANCE, 0);
                y1Inner = fullVerticalRequired ? 0 : Math.Max(position.Y - MAX_LIGHT_SPREAD_DISTANCE, 0);
                z1Inner = Math.Max(position.Z - MAX_LIGHT_SPREAD_DISTANCE, 0);
                x2Inner = Math.Min(position.X + MAX_LIGHT_SPREAD_DISTANCE, WorldData.SizeInBlocksX - 1);
                y2Inner = fullVerticalRequired ? Chunk.CHUNK_HEIGHT - 1 : Math.Min(position.Y + MAX_LIGHT_SPREAD_DISTANCE, Chunk.CHUNK_HEIGHT - 1);
                z2Inner = Math.Min(position.Z + MAX_LIGHT_SPREAD_DISTANCE, WorldData.SizeInBlocksZ - 1);

                //figure out the chunk at each bottom corner of the light box (this will be at most 4, and will usually be the full 4 unless change is near a world edge or very close to the center of a chunk)
                //-this works because the chunk size is 32 therefore we know no more than 4 chunks can be affected when the max light spread distance is 15
                var chunkBackLeft = WorldData.Chunks[x1Inner / Chunk.CHUNK_SIZE, z1Inner / Chunk.CHUNK_SIZE];
                var chunkFrontLeft = WorldData.Chunks[x1Inner / Chunk.CHUNK_SIZE, z2Inner / Chunk.CHUNK_SIZE];
                var chunkFrontRight = WorldData.Chunks[x2Inner / Chunk.CHUNK_SIZE, z2Inner / Chunk.CHUNK_SIZE];
                var chunkBackRight = WorldData.Chunks[x2Inner / Chunk.CHUNK_SIZE, z1Inner / Chunk.CHUNK_SIZE];

                chunksAffected.Enqueue(chunkContainingChange); //queue the chunk containing the changing block first, this makes the change seem more responsive to the user
                if (!chunksAffected.Contains(chunkBackLeft)) chunksAffected.Enqueue(chunkBackLeft);
                if (!chunksAffected.Contains(chunkFrontLeft)) chunksAffected.Enqueue(chunkFrontLeft);
                if (!chunksAffected.Contains(chunkFrontRight)) chunksAffected.Enqueue(chunkFrontRight);
                if (!chunksAffected.Contains(chunkBackRight)) chunksAffected.Enqueue(chunkBackRight);

                //adjacent chunks need to be queued first for block removes on a chunk border, otherwise we would briefly see a blank face on the adjacent block
                if (isRemove && position.IsOnChunkBorder) chunksAffected.Enqueue(chunksAffected.Dequeue()); //dequeue the first chunk and requeue it last
            }
            else //cuboid change
            {
                //determine 2 diagonal corners of the cuboid on the bottom plane
                int x1 = Math.Min(position.X, positionCuboid.Value.X);
                int z1 = Math.Min(position.Z, positionCuboid.Value.Z);
                int x2 = Math.Max(position.X, positionCuboid.Value.X);
                int z2 = Math.Max(position.Z, positionCuboid.Value.Z);

                x1Inner = Math.Max(x1 - MAX_LIGHT_SPREAD_DISTANCE, 0);
                y1Inner = 0;
                z1Inner = Math.Max(z1 - MAX_LIGHT_SPREAD_DISTANCE, 0);
                x2Inner = Math.Min(x2 + MAX_LIGHT_SPREAD_DISTANCE, WorldData.SizeInBlocksX - 1);
                y2Inner = Chunk.CHUNK_HEIGHT - 1;
                z2Inner = Math.Min(z2 + MAX_LIGHT_SPREAD_DISTANCE, WorldData.SizeInBlocksZ - 1);

                var chunk1 = WorldData.Chunks[x1Inner / Chunk.CHUNK_SIZE, z1Inner / Chunk.CHUNK_SIZE];
                var chunk2 = WorldData.Chunks[x2Inner / Chunk.CHUNK_SIZE, z2Inner / Chunk.CHUNK_SIZE];
                //loop through and add every chunk contained in the inner light box to the queue of affected chunks
                for (int x = chunk1.Coords.X; x <= chunk2.Coords.X; x++)
                {
                    for (int z = chunk1.Coords.Z; z <= chunk2.Coords.Z; z++)
                    {
                        chunksAffected.Enqueue(WorldData.Chunks[x, z]);
                    }
                }
            }

            //outer box is the same for single block or cuboid
            int x1Outer = Math.Max(x1Inner - 1, 0);
            int y1Outer = Math.Max(y1Inner - 1, 0);
            int z1Outer = Math.Max(z1Inner - 1, 0);
            int x2Outer = Math.Min(x2Inner + 1, WorldData.SizeInBlocksX - 1);
            int y2Outer = Math.Min(y2Inner + 1, Chunk.CHUNK_HEIGHT - 1);
            int z2Outer = Math.Min(z2Inner + 1, WorldData.SizeInBlocksZ - 1);

            ResetLightBoxSources(x1Inner, x2Inner, y1Inner, y2Inner, z1Inner, z2Inner, null, WorldData.SkyLightMap, WorldData.ItemLightMap);

            //loop through the outer light box to re-propagate, this pulls back in surrounding light as needed because the outer box is one larger
            for (int x = x1Outer; x < x2Outer; x++)
            {
                for (int y = y1Outer; y < y2Outer; y++)
                {
                    for (int z = z1Outer; z < z2Outer; z++)
                    {
                        //propagate sky light
                        byte skyLightStrength = WorldData.SkyLightMap[x, y, z];
                        if (skyLightStrength > 1) PropagateLightDynamic(x, y, z, skyLightStrength, WorldData.SkyLightMap); //if strength > 1, check 6 adjacent neighbors and propagate recursively as needed
                        //propagate item light
                        byte itemLightStrength = WorldData.ItemLightMap[x, y, z];
                        if (itemLightStrength > 1) PropagateLightDynamic(x, y, z, itemLightStrength, WorldData.ItemLightMap); //if strength > 1, check 6 adjacent neighbors and propagate recursively as needed
                    }
                }
            }

            Debug.WriteLine("UpdateLightBox took {0}ms for {1} (Y range {2}) Queueing {3} chunks", stopwatch.ElapsedMilliseconds, position, y2Inner - y1Inner + 1, chunksAffected.Count);

            return chunksAffected;
        }
Exemple #24
0
 protected void Write(ref Position position)
 {
     Write(position.ToByteArray(), Position.SIZE);
 }
Exemple #25
0
 /// <summary>Get the exact distance from the supplied coords.</summary>
 public float GetDistanceExact(ref Position position)
 {
     return (float)Math.Sqrt(Math.Pow(X - position.X, 2) + Math.Pow(Y - position.Y, 2) + Math.Pow(Z - position.Z, 2));
 }
Exemple #26
0
        /// <summary>Only called for SinglePlayer and Servers.</summary>
        private void WaterExpand()
        {
            Debug.WriteLine("Water expanding in chunk {0}...", Coords);
            var newWater = new List<Position>();
            for (var i = 0; i < CHUNK_SIZE; i++)
            {
                for (var j = 0; j < CHUNK_HEIGHT; j++)
                {
                    for (var k = 0; k < CHUNK_SIZE; k++)
                    {
                        if (Blocks[i, j, k].Type != Block.BlockType.Water) continue;
                        var belowCurrent = new Position();
                        for (var q = 0; q < 5; q++)
                        {
                            Position adjacent;
                            switch (q)
                            {
                                case 0:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j - 1, Coords.WorldCoordsZ + k);
                                    belowCurrent = adjacent;
                                    break;
                                case 1:
                                    adjacent = new Position(Coords.WorldCoordsX + i + 1, j, Coords.WorldCoordsZ + k);
                                    break;
                                case 2:
                                    adjacent = new Position(Coords.WorldCoordsX + i - 1, j, Coords.WorldCoordsZ + k);
                                    break;
                                case 3:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j, Coords.WorldCoordsZ + k + 1);
                                    break;
                                default:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j, Coords.WorldCoordsZ + k - 1);
                                    break;
                            }

                            if (newWater.Contains(adjacent)) continue;

                            //if there's air or water below the current block, don't spread sideways
                            if (q != 0 && belowCurrent.IsValidBlockLocation && (Blocks[belowCurrent].Type == Block.BlockType.Air || Blocks[belowCurrent].Type == Block.BlockType.Water)) continue;
                            if (adjacent.IsValidBlockLocation && adjacent.GetBlock().Type == Block.BlockType.Air) newWater.Add(adjacent);
                        }
                    }
                }
            }

            if (newWater.Count == 0)
            {
                WaterExpanding = false;
                Debug.WriteLine("Water finished expanding in chunk {0}", Coords);
                return;
            }

            var addBlocks = new List<AddBlock>();
            Settings.ChunkUpdatesDisabled = true; //change blocks while updates are disabled so chunk is only rebuilt once
            foreach (var newWaterPosition in newWater.Where(newWaterCoords => newWaterCoords.GetBlock().Type != Block.BlockType.Water))
            {
                WorldData.PlaceBlock(newWaterPosition, Block.BlockType.Water);
                if (Config.IsServer)
                {
                    var temp = newWaterPosition;
                    addBlocks.Add(new AddBlock(ref temp, Block.BlockType.Water));
                }
            }
            Settings.ChunkUpdatesDisabled = false;

            if (Config.IsServer && addBlocks.Count > 0)
            {
                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti {ConnectedPlayer = player};
                    addBlockMulti.Blocks.AddRange(addBlocks);
                    addBlockMulti.Send();
                }
            }
        }
Exemple #27
0
 /// <summary>Get a chunk from the array. Based on world coords.</summary>
 public Chunk this[Position position]
 {
     get { return _chunks[position.X / Chunk.CHUNK_SIZE, position.Z / Chunk.CHUNK_SIZE]; }
 }
Exemple #28
0
        /// <summary>Decide if this block face needs to be added to a VBO and rendered. XYZ are world relative coordinates.</summary>
        private void AddBlockFace(ref Block block, Face face, int x, int y, int z)
        {
            int adjacentX = x, adjacentY = y, adjacentZ = z;

            //check if surface is adjacent to another solid texture (if so dont draw it)
            switch (face)
            {
                case Face.Right: adjacentX++; break;
                case Face.Left: adjacentX--; break;
                case Face.Top: adjacentY++; break;
                case Face.Bottom: adjacentY--; break;
                case Face.Front: adjacentZ++; break;
                default: adjacentZ--; break; //back
            }

            //todo: possible optimization, get the Block from this.Blocks array when x/z are NOT on chunk edge, would result in not looking up chunk in World.GetBlock 99% of time, but tiny bit more checks for blocks on edges, try it out some other time
            //-could accept a bool param needToCheckOverChunkEdge, the DTL logic would always pass false for it because its alrdy figured it out, so both ways get the optimization
            #region todo: none of this would need to be checked for the DTL added faces, could grab the block straight out of this chunk instead, wouldnt need to check adjacent block either
            if (!WorldData.IsValidBlockLocation(adjacentX, adjacentY, adjacentZ)) return; //adjacent block is outside the world so theres no need to render this face
            var adjacentBlock = WorldData.GetBlock(adjacentX, adjacentY, adjacentZ);
            if (!adjacentBlock.IsTransparent) return; //no need to render this face because the adjacent face is not transparent

            //dont render inner faces of neighboring transparent blocks of same type, makes trees hollow and doesnt draw additional water faces under water
            //improves fps by limiting the number of faces needed and looks better on weak laptop gpu's as well (note: we know the adjacent face is transparent if we get this far)
            if (block.IsTransparent && block.Type == adjacentBlock.Type) return;
            #endregion

            var texture = Block.FaceTexture(block.Type, face);
            //check if there is an existing vbo for this chunk/texture to add this face to
            if (_chunkVbos[(int)texture] == null)
            {
                //vbo not created yet for this chunk/texture so we need to start one
                _chunkVbos[(int)texture] = new ChunkVbo(ref block, TextureLoader.GetBlockTexture(texture));
            }

            //we need position 4 no matter what, so retrieve it here, position 4 is used for the lighting values of all 4 vertices of this face when smooth lighting is off
            byte lightColor = WorldData.GetBlockLightColor(adjacentX, adjacentY, adjacentZ);

            _shortestFaceHeightTemp = Math.Min(_shortestFaceHeightTemp, y);

            if (block.Type == Block.BlockType.Leaves || block.Type == Block.BlockType.SnowLeaves)
            {
                //dont bother with smooth lighting for leaves, it would rarely matter anyway
                if (face == Face.Bottom) lightColor = (byte)(lightColor * 0.6); //make bottom leaves faces darker by giving them 60% of the light they would otherwise have
                BlockRender.AddBlockFaceToVbo(_chunkVbos[(int)texture], face, x, y, z, lightColor);
                return;
            }

            if (Config.SmoothLighting)
            {
                #region Smooth Lighting
                //SMOOTH LIGHTING MAP (block 4 is used by all 4 vertices, blocks 1,3,5,7 are used by 2 vertices each, blocks 0,2,6,8 are used by one vertex only)
                //	0  1  2
                //   v1 v0
                //	3  4  5
                //   v2 v3
                //	6  7  8
                var xyz = new Position[9];
                var smoothLighting = new byte[9];
                smoothLighting[4] = lightColor; //we already have the directly adjacent color which goes in position 4
                switch (face)
                {
                    case Face.Right: //X is constant
                        xyz[0] = new Position(adjacentX, adjacentY + 1, adjacentZ + 1);
                        xyz[1] = new Position(adjacentX, adjacentY + 1, adjacentZ);
                        xyz[2] = new Position(adjacentX, adjacentY + 1, adjacentZ - 1);
                        xyz[3] = new Position(adjacentX, adjacentY, adjacentZ + 1);
                        xyz[5] = new Position(adjacentX, adjacentY, adjacentZ - 1);
                        xyz[6] = new Position(adjacentX, adjacentY - 1, adjacentZ + 1);
                        xyz[7] = new Position(adjacentX, adjacentY - 1, adjacentZ);
                        xyz[8] = new Position(adjacentX, adjacentY - 1, adjacentZ - 1);
                        break;
                    case Face.Left: //X is constant
                        xyz[0] = new Position(adjacentX, adjacentY + 1, adjacentZ - 1);
                        xyz[1] = new Position(adjacentX, adjacentY + 1, adjacentZ);
                        xyz[2] = new Position(adjacentX, adjacentY + 1, adjacentZ + 1);
                        xyz[3] = new Position(adjacentX, adjacentY, adjacentZ - 1);
                        xyz[5] = new Position(adjacentX, adjacentY, adjacentZ + 1);
                        xyz[6] = new Position(adjacentX, adjacentY - 1, adjacentZ - 1);
                        xyz[7] = new Position(adjacentX, adjacentY - 1, adjacentZ);
                        xyz[8] = new Position(adjacentX, adjacentY - 1, adjacentZ + 1);
                        break;
                    case Face.Top: //Y is constant
                        xyz[0] = new Position(adjacentX - 1, adjacentY, adjacentZ - 1);
                        xyz[1] = new Position(adjacentX, adjacentY, adjacentZ - 1);
                        xyz[2] = new Position(adjacentX + 1, adjacentY, adjacentZ - 1);
                        xyz[3] = new Position(adjacentX - 1, adjacentY, adjacentZ);
                        xyz[5] = new Position(adjacentX + 1, adjacentY, adjacentZ);
                        xyz[6] = new Position(adjacentX - 1, adjacentY, adjacentZ + 1);
                        xyz[7] = new Position(adjacentX, adjacentY, adjacentZ + 1);
                        xyz[8] = new Position(adjacentX + 1, adjacentY, adjacentZ + 1);
                        break;
                    case Face.Bottom: //Y is constant
                        xyz[0] = new Position(adjacentX - 1, adjacentY, adjacentZ + 1);
                        xyz[1] = new Position(adjacentX, adjacentY, adjacentZ + 1);
                        xyz[2] = new Position(adjacentX + 1, adjacentY, adjacentZ + 1);
                        xyz[3] = new Position(adjacentX - 1, adjacentY, adjacentZ);
                        xyz[5] = new Position(adjacentX + 1, adjacentY, adjacentZ);
                        xyz[6] = new Position(adjacentX - 1, adjacentY, adjacentZ - 1);
                        xyz[7] = new Position(adjacentX, adjacentY, adjacentZ - 1);
                        xyz[8] = new Position(adjacentX + 1, adjacentY, adjacentZ - 1);
                        break;
                    case Face.Front: //Z is constant
                        xyz[0] = new Position(adjacentX - 1, adjacentY + 1, adjacentZ);
                        xyz[1] = new Position(adjacentX, adjacentY + 1, adjacentZ);
                        xyz[2] = new Position(adjacentX + 1, adjacentY + 1, adjacentZ);
                        xyz[3] = new Position(adjacentX - 1, adjacentY, adjacentZ);
                        xyz[5] = new Position(adjacentX + 1, adjacentY, adjacentZ);
                        xyz[6] = new Position(adjacentX - 1, adjacentY - 1, adjacentZ);
                        xyz[7] = new Position(adjacentX, adjacentY - 1, adjacentZ);
                        xyz[8] = new Position(adjacentX + 1, adjacentY - 1, adjacentZ);
                        break;
                    default: //Back; Z is constant
                        xyz[0] = new Position(adjacentX + 1, adjacentY + 1, adjacentZ);
                        xyz[1] = new Position(adjacentX, adjacentY + 1, adjacentZ);
                        xyz[2] = new Position(adjacentX - 1, adjacentY + 1, adjacentZ);
                        xyz[3] = new Position(adjacentX + 1, adjacentY, adjacentZ);
                        xyz[5] = new Position(adjacentX - 1, adjacentY, adjacentZ);
                        xyz[6] = new Position(adjacentX + 1, adjacentY - 1, adjacentZ);
                        xyz[7] = new Position(adjacentX, adjacentY - 1, adjacentZ);
                        xyz[8] = new Position(adjacentX - 1, adjacentY - 1, adjacentZ);
                        break;
                }

                //need to know if blocks on positions 1,3,5,7 are transparent, each value is used twice
                //-for example neither 1 or 5 are transparent, then vertex0 should not have any influence from light at position 2 and should use the darkest value instead for a dark corner
                //		-position 2 might be on the other side of a wall and then we get sunlight coming into ceiling corners etc
                //-if the coords of any of these positions are outside the world then consider them transparent
                bool transparent1 = !xyz[1].IsValidBlockLocation || xyz[1].GetBlock().IsTransparent;
                bool transparent3 = !xyz[3].IsValidBlockLocation || xyz[3].GetBlock().IsTransparent;
                bool transparent5 = !xyz[5].IsValidBlockLocation || xyz[5].GetBlock().IsTransparent;
                bool transparent7 = !xyz[7].IsValidBlockLocation || xyz[7].GetBlock().IsTransparent;

                smoothLighting[0] = transparent1 || transparent3 ? xyz[0].LightColor : Lighting.DARKEST_COLOR;
                smoothLighting[1] = xyz[1].LightColor;
                smoothLighting[2] = transparent1 || transparent5 ? xyz[2].LightColor : Lighting.DARKEST_COLOR;
                smoothLighting[3] = xyz[3].LightColor;
                smoothLighting[5] = xyz[5].LightColor;
                smoothLighting[6] = transparent3 || transparent7 ? xyz[6].LightColor : Lighting.DARKEST_COLOR;
                smoothLighting[7] = xyz[7].LightColor;
                smoothLighting[8] = transparent5 || transparent7 ? xyz[8].LightColor : Lighting.DARKEST_COLOR;

                //average 4 colors to get the color for each vertex
                var v0Color = (byte)((smoothLighting[1] + smoothLighting[2] + smoothLighting[4] + smoothLighting[5]) / 4);
                var v1Color = (byte)((smoothLighting[0] + smoothLighting[1] + smoothLighting[3] + smoothLighting[4]) / 4);
                var v2Color = (byte)((smoothLighting[3] + smoothLighting[4] + smoothLighting[6] + smoothLighting[7]) / 4);
                var v3Color = (byte)((smoothLighting[4] + smoothLighting[5] + smoothLighting[7] + smoothLighting[8]) / 4);

                BlockRender.AddBlockFaceToVbo(_chunkVbos[(int)texture], face, x, y, z, v0Color, v1Color, v2Color, v3Color);
                #endregion
            }
            else //use simple lighting
            {
                BlockRender.AddBlockFaceToVbo(_chunkVbos[(int)texture], face, x, y, z, lightColor);
            }
        }
 public static void BuildTree(Position position)
 {
     var leafPosition1 = new Position(position.X - 3, position.Y + 4, position.Z - 3);
     var leafPosition2 = new Position(position.X + 3, position.Y + 8, position.Z + 3);
     WorldData.PlaceCuboid(leafPosition1, leafPosition2, WorldData.WorldType == WorldType.Winter ? Block.BlockType.SnowLeaves : Block.BlockType.Leaves, true);
     WorldData.PlaceCuboid(position, new Position(position.X, position.Y + 6, position.Z), Block.BlockType.Tree, true);
 }
Exemple #30
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var bytes = ReadStream(DataLength);
                    Position = new Position(bytes, 0);
                    StructureType = (StructureType)BitConverter.ToUInt16(bytes, Position.SIZE);
                    FrontFace = (Facing)BitConverter.ToUInt16(bytes, Position.SIZE + sizeof(ushort));
                }
            }

            switch (StructureType)
            {
                case StructureType.Tree:
                    StructureBuilder.BuildTree(Position);
                    break;
                case StructureType.Tower:
                    StructureBuilder.BuildCastle(Position, Radius, 6, Block.BlockType.Cobble, FrontFace);
                    break;
                case StructureType.SmallKeep:
                    StructureBuilder.BuildCastle(Position, Radius, 8, Block.BlockType.Cobble, FrontFace);
                    break;
                case StructureType.LargeKeep:
                    StructureBuilder.BuildCastle(Position, Radius, 10, Block.BlockType.SteelPlate, FrontFace);
                    break;
            }

            if (Config.IsServer)
            {
                foreach (var player in Controller.Players.Values)
                {
                    new AddStructure(Position, StructureType, FrontFace) { ConnectedPlayer = player }.Send();
                }
            }
            else
            {
                //determine the corner coords for this structure; pass them for light calc and chunk queueing (note: the Y doesnt matter so use 0 for both)
                WorldData.ModifyLightAndQueueChunksForCuboidChange(new Position(Position.X - Radius, 0, Position.Z - Radius), new Position(Position.X + Radius, 0, Position.Z + Radius));
                Sounds.Audio.PlaySound(Sounds.SoundType.AddBlock);
            }
        }