Java tutorial
/******************************************************************************* * Copyright 2014 Logan Gorence <loganjohngorence@gmail.com> * * 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.minetweak.world.characters; import org.minetweak.GameObject; import org.minetweak.blocks.Block; import org.minetweak.blocks.BlockConstructor; import org.minetweak.blocks.BlockManager; import org.minetweak.blocks.BlockType; import org.minetweak.client.sounds.SoundManager; import org.minetweak.datastructures.AABB; import org.minetweak.game.ControlSettings; import org.minetweak.game.Game; import org.minetweak.inventory.DefaultPlayerInventory; import org.minetweak.inventory.Inventory; import org.minetweak.inventory.InventoryIO; import org.minetweak.inventory.SharedInventoryContent; import org.minetweak.items.ItemManager; import org.minetweak.math.MathHelper; import org.minetweak.math.RayBlockIntersection; import org.minetweak.math.Vec3f; import org.minetweak.math.Vec3i; import org.minetweak.utilities.IOUtilities; import org.minetweak.world.Chunk; import org.minetweak.world.ChunkManager; import org.minetweak.inventory.InventoryItem; import org.minetweak.rendering.Camera; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; /** * @author Logan Gorence */ public class Player extends GameObject { private static BlockManager _blockManager = BlockManager.getInstance(); private Camera _camera; /** * Postion of the player absolute in the world */ private float x, y, z; private Vec3f _position; /** * Rotation of the players head, in radians */ private float rotX, rotY; // Radians private float bobbing, bobbingProcess; /* Spawn Point */ private Vec3f _spawnPoint; /* Movement variables */ private float speedForward = 0.0f; private float speedSide = 0.0f; private float maxSpeed = 6.0f; private float eyeHeight = 1.7f; private float playerHeight = 1.85f; private float acceleration = 15.0f; private float ySpeed = 0.0f; private boolean onGround = false; private boolean _flying = false; private int _rotationSegment = 0; /* Body */ private CharacterBody _body; /* Editing */ private float _rayCastLength; private AABB _intersectionTestingAABB; private AABB _aimedBlockAABB; private byte _aimedBlockType; private Vec3i _aimedBlockPosition; private Vec3i _aimedAdjacentBlockPosition; private float _aimedBlockHealth; private AABB _rayAABB; private ChunkManager _chunkManager; private InventoryItem _selectedItem; private int _selectedInventoryItemIndex = 0; private boolean _airSmashed; private boolean _destroying; /* Inventory */ private DefaultPlayerInventory _inventory; private SharedInventoryContent _sharedInventoryContent; public Player(float x, float y, float z) { _position = new Vec3f(x, y, z); _spawnPoint = new Vec3f(x, y, z); _chunkManager = Game.getInstance().getWorld().getChunkManager(); _body = new CharacterBody(); _camera = new Camera(); _camera.setFovy(Game.getInstance().getConfiguration().getFOVY()); this.x = x; this.y = y; this.z = z; _inventory = new DefaultPlayerInventory(); _sharedInventoryContent = new SharedInventoryContent(4 * 9); _inventory.setSharedContent(_sharedInventoryContent); _rayCastLength = Game.getInstance().getConfiguration().getMaximumPlayerEditingDistance(); _rayAABB = new AABB(new Vec3f(), new Vec3f()); _aimedBlockPosition = new Vec3i(0, -1, 0); _aimedBlockAABB = new AABB(new Vec3f(), new Vec3f()); _intersectionTestingAABB = new AABB(new Vec3f(), new Vec3f()); setSelectedInventoryItemIndex(0); } public Player(Vec3f spawnPoint) { this(spawnPoint.x(), spawnPoint.y(), spawnPoint.z()); } public Camera getFirstPersonCamera() { return _camera; } public void setPosition(Vec3f position) { this.x = position.x(); this.y = position.y(); this.z = position.z(); } /** * The returned vector is the single instanced vector representing the * players position. DO NOT MODIFY THIS VECTOR * * @return the position of the player */ public Vec3f getPosition() { return _position; } private Vec3f getSpawnPoint() { return _spawnPoint; } @Override public void render() { GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glDepthFunc(GL11.GL_LEQUAL); GL11.glEnable(GL11.GL_BLEND); GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); if (_aimedBlockPosition.y() != -1) { GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); GL11.glDisable(GL11.GL_TEXTURE_2D); _aimedBlockAABB.render(0.0f, 0.0f, 0.0f, 0.1f); GL11.glEnable(GL11.GL_TEXTURE_2D); } if (_selectedItem != null) { GL11.glPushMatrix(); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_CULL_FACE); _body.transformToRightHand(); /* Prepare the light buffer */ Chunk c = Game.getInstance().getWorld().getChunkManager().getChunkContaining( MathHelper.floor(_position.x()), MathHelper.floor(_position.y()) + 1, MathHelper.floor(_position.z()), false, false, false); if (c != null) { c.getLightBuffer().setReferencePoint(MathHelper.floor(_position.x()), MathHelper.floor(_position.y()) + 1, MathHelper.floor(_position.z())); /* Render the object, with the lightbuffer */ _selectedItem.renderHoldableObject(c.getLightBuffer()); } GL11.glEnable(GL11.GL_CULL_FACE); GL11.glPopMatrix(); } GL11.glDisable(GL11.GL_DEPTH_TEST); } @Override public void update() { physics(); movement(); _position.set(x, y, z); /* Update the camera */ _camera.setPosition(x, y + eyeHeight, z); _camera.setRotation(rotX, rotY, bobbing); _body.update(); _body.disableUsingRightHand(); if (_selectedItem != null) { _body._animationSpeedRightHand = _selectedItem.getAnimationSpeed(); } else { _body._animationSpeedRightHand = 1.0f; } rayCastBlock(); if (ControlSettings.isActionHold(ControlSettings.SMASH)) { smash(); } else { _destroying = false; _airSmashed = false; } } private void movement() { float step = Game.getInstance().getStep(); float factor = (onGround ? 1.0f : 0.5f); float accelerationStep = acceleration * step; float xStep = 0.0f; float zStep = 0.0f; // Forward movement { if (ControlSettings.isActionHold(ControlSettings.MOVE_FORWARD)) { speedForward = Math.min(maxSpeed * (_flying ? 3.0f : 1.0f), speedForward + acceleration * step); } else if (ControlSettings.isActionHold(ControlSettings.MOVE_BACK)) { speedForward = Math.max(-maxSpeed * (_flying ? 3.0f : 1.0f), speedForward - acceleration * step); } else { if (speedForward != 0.0f) { if (speedForward < 0.0f) { speedForward += factor * accelerationStep; if (speedForward > 0.0f) { speedForward = 0.0f; } } else { speedForward -= factor * accelerationStep; if (speedForward < 0.0f) { speedForward = 0.0f; } } } } float xAdd = MathHelper.cos(rotY) * speedForward * step; float zAdd = MathHelper.sin(-rotY) * speedForward * step; xStep += xAdd; zStep += zAdd; } // Side movement { if (ControlSettings.isActionHold(ControlSettings.MOVE_LEFT)) { speedSide = Math.max(-maxSpeed * (_flying ? 3.0f : 1.0f), speedSide - acceleration * step); } else if (ControlSettings.isActionHold(ControlSettings.MOVE_RIGHT)) { speedSide = Math.min(maxSpeed * (_flying ? 3.0f : 1.0f), speedSide + acceleration * step); } else { if (speedSide != 0.0f) { if (speedSide < 0.0f) { speedSide += factor * accelerationStep; if (speedSide > 0.0f) { speedSide = 0.0f; } } else { speedSide -= factor * accelerationStep; if (speedSide < 0.0f) { speedSide = 0.0f; } } } } float xAdd = (float) (MathHelper.sin(rotY)) * speedSide * step; float zAdd = (float) (MathHelper.cos(rotY)) * speedSide * step; xStep += xAdd; zStep += zAdd; } x += xStep; z += zStep; Chunk bc = null; byte wall = 0; byte wall2 = 0; { int xx = MathHelper.floor(x), yy = MathHelper.floor(y + 0.1f), zz = MathHelper.floor(z); bc = _chunkManager.getChunkContaining(xx, yy, zz, false, true, true); if (bc != null) { wall = bc.getBlockTypeAbsolute(xx, yy, zz, false, false, false); } } { int xx = MathHelper.floor(x), yy = MathHelper.floor(y + 0.1f) + 1, zz = MathHelper.floor(z); if (bc != null) { wall2 = bc.getBlockTypeAbsolute(xx, yy, zz, false, false, false); } else { bc = _chunkManager.getChunkContaining(xx, yy, zz, false, true, true); if (bc != null) { wall2 = bc.getBlockTypeAbsolute(xx, yy, zz, false, false, false); } } } if ((wall > 0 && BlockManager.getInstance().getBlockType(wall).isSolid()) || (wall2 > 0 && BlockManager.getInstance().getBlockType(wall2).isSolid())) { x -= xStep; z -= zStep; speedForward *= -0.2f; speedSide *= -0.2f; } float speed = MathHelper.sqrt(speedForward * speedForward + speedSide * speedSide); speed /= maxSpeed; bobbingProcess += step * 10.0f; bobbingProcess = MathHelper.simplifyRadians(bobbingProcess); float dx = Mouse.getDX(); float dy = Mouse.getDY(); dx /= 300.0f; dy /= 300.0f; float rotSeg = _rotationSegment; _rotationSegment = MathHelper.round(rotY / MathHelper.f_PI * 10.0f); rotY -= dx; rotX += dy; /* If it changes from chunk, recheck visible chunks */ if (MathHelper.floorDivision(MathHelper.floor(x + xStep), Chunk.CHUNK_SIZE_HORIZONTAL) != MathHelper .floorDivision(MathHelper.floor(x), Chunk.CHUNK_SIZE_HORIZONTAL) || MathHelper.floorDivision(MathHelper.floor(z + zStep), Chunk.CHUNK_SIZE_HORIZONTAL) != MathHelper .floorDivision(MathHelper.floor(z), Chunk.CHUNK_SIZE_HORIZONTAL) || _rotationSegment != rotSeg) { Game.getInstance().getWorld().selectLocalChunks(); Game.getInstance().getWorld().checkForNewVisibleChunks(); } rotY = MathHelper.simplifyRadians(rotY); rotX = MathHelper.clamp(rotX, -MathHelper.f_PI / 2.001f, MathHelper.f_PI / 2.001f); if (_flying) { bobbing *= 0.8f; } else { bobbing = MathHelper.sin(bobbingProcess) * 0.03f * speed * (MathHelper.f_PI / 2.0f - Math.abs(rotX)) / (MathHelper.f_PI / 2.0f); } } private void physics() { float step = Game.getInstance().getStep(); ChunkManager chunkManager = Game.getInstance().getWorld().getChunkManager(); byte support = chunkManager.getBlock(MathHelper.floor(x), MathHelper.floor(y) - 1, MathHelper.floor(z), false, false, false); byte subSupport = chunkManager.getBlock(MathHelper.floor(x), MathHelper.floor(y) - 2, MathHelper.floor(z), false, false, false); float supportHeight = Float.NEGATIVE_INFINITY; if (ControlSettings.isActionHold(ControlSettings.JUMP)) { if (onGround) { ySpeed = 14f; onGround = false; } else if (_flying) { ySpeed += 14f * step; } } else if (_flying && ControlSettings.isActionHold(ControlSettings.CROUCH)) { ySpeed -= 14f * step; } if (support > 0) { if (BlockManager.getInstance().getBlockType(support).isSolid()) { supportHeight = MathHelper.floor(y); onGround = false; } } else if (subSupport > 0) { if (BlockManager.getInstance().getBlockType(subSupport).isSolid()) { supportHeight = MathHelper.floor(y) - 1; onGround = false; } } if (supportHeight > y) { y = supportHeight; ySpeed = 0.0f; onGround = true; } else { if (!_flying) { ySpeed -= step * 55f; } else { float newSpeedY = ySpeed * 0.1f; float diffY = newSpeedY - ySpeed; ySpeed += diffY * step; } y += ySpeed * step; if (ySpeed > 0.0f) { int headbangY = MathHelper.floor(y + 0.1f) + 2; byte headbang = chunkManager.getBlock(MathHelper.floor(x), headbangY, MathHelper.floor(z), false, false, false); if (headbang > 0 && (headbangY < y + playerHeight)) { y = (float) headbangY - playerHeight; ySpeed = 0.0f; } } { onGround = false; if (supportHeight >= y) { y = supportHeight; ySpeed = 0.0f; onGround = true; } } } } private void rayCastBlock() { float rayLenSquared = _rayCastLength * _rayCastLength; Vec3f rayDirection = _camera.getLookDirection(); Vec3f rayOrigin = _camera.getPosition(); rayDirection.normalise(); /* Construct the AABB for the ray cast */ _rayAABB.getPosition().set(rayOrigin); _rayAABB.getPosition().addFactor(rayDirection, _rayCastLength * 0.5f); _rayAABB.getDimensions().set(Math.abs(rayDirection.x()), Math.abs(rayDirection.y()), Math.abs(rayDirection.z())); _rayAABB.getDimensions().scale(_rayCastLength * 0.5f); int aabbX = MathHelper.round(_rayAABB.getPosition().x()); int aabbY = MathHelper.round(_rayAABB.getPosition().y()); int aabbZ = MathHelper.round(_rayAABB.getPosition().z()); RayBlockIntersection.Intersection closestIntersection = null; byte closestBlock = 0; /* Iterate over all possible candidates for the raycast */ Vec3f v = new Vec3f(); Vec3i newAimedBlockPosition = new Vec3i(); byte bl = 0; boolean special = false; Chunk chunk = Game.getInstance().getWorld().getChunkManager().getChunkContaining(aabbX, aabbY, aabbZ, false, false, false); if (chunk == null) { return; } for (int x = MathHelper.floor(_rayAABB.minX()); x <= MathHelper.ceil(_rayAABB.maxX()); ++x) { for (int y = MathHelper.floor(_rayAABB.minY()); y <= MathHelper.ceil(_rayAABB.maxY()); ++y) { for (int z = MathHelper.floor(_rayAABB.minZ()); z <= MathHelper.ceil(_rayAABB.maxZ()); ++z) { bl = chunk.getBlockTypeAbsolute(x, y, z, false, false, false); if (bl == 0 || bl == -1) { continue; } special = chunk.isBlockSpecialAbsolute(x, y, z); Block block = null; if (special) { block = chunk.getSpecialBlockAbsolute(x, y, z); if (block.isMoving()) { continue; } _intersectionTestingAABB.set(block.getAABB()); } v.set(x + 0.5f, y + 0.5f, z + 0.5f); v.sub(getPosition()); float lenSquared = v.lengthSquared(); if (lenSquared < rayLenSquared) { if (!special) { _intersectionTestingAABB.getPosition().set(x + 0.5f, y + 0.5f, z + 0.5f); _intersectionTestingAABB.getDimensions() .set(_blockManager.getBlockType(bl).getDimensions()); _intersectionTestingAABB.recalcVertices(); } /* Perform the raycast */ List<RayBlockIntersection.Intersection> intersections = RayBlockIntersection .executeIntersection(x, y, z, _intersectionTestingAABB, rayOrigin, rayDirection); if (!intersections.isEmpty()) { if (closestIntersection == null || intersections.get(0).getDistance() < closestIntersection.getDistance()) { closestIntersection = intersections.get(0); closestBlock = bl; newAimedBlockPosition.set(x, y, z); if (special) { _aimedBlockAABB.set(block.getAABB()); } else { BlockType aimedBlockType = _blockManager.getBlockType(bl); _aimedBlockAABB.getPosition().set(_aimedBlockPosition.x(), _aimedBlockPosition.y(), _aimedBlockPosition.z()) .add(aimedBlockType.getCenter()); _aimedBlockAABB.getDimensions().set(aimedBlockType.getDimensions()); } } } } } } } if (closestIntersection != null) { if (!_aimedBlockPosition.equals(newAimedBlockPosition)) { _aimedBlockHealth = _blockManager.getBlockType(closestBlock).getResistance(); _aimedBlockPosition.set(newAimedBlockPosition); _aimedBlockType = closestBlock; _aimedBlockAABB.recalcVertices(); } _aimedAdjacentBlockPosition = closestIntersection.calcAdjacentBlockPos(); } else { _aimedBlockPosition.setY(-1); _aimedAdjacentBlockPosition = null; _aimedBlockType = 0; } if (closestIntersection != null && _selectedItem != null && _aimedBlockPosition.y() != -1) { _body.setBlockDistance(closestIntersection.getDistance()); } else { _body.setBlockDistance(0.0f); } } public DefaultPlayerInventory getInventory() { return _inventory; } private void setSelectedInventoryItemIndex(int i) { Inventory.InventoryPlace oldPlace = _inventory.getInventoryPlace(_selectedInventoryItemIndex); if (oldPlace != null) { if (oldPlace.getItem() != null) { _body.forceDisableUsingRightHand(); } } _selectedInventoryItemIndex = i; Inventory.InventoryPlace newPlace = _inventory.getInventoryPlace(_selectedInventoryItemIndex); if (newPlace != null) { if (newPlace.isStack()) { int itemType = newPlace.getStack().getItemType(); _selectedItem = ItemManager.getInstance().getInventoryItem((short) itemType); } else { _selectedItem = newPlace.getItem(); } } else { _selectedItem = null; } } public void inventoryContentChanged() { setSelectedInventoryItemIndex(_selectedInventoryItemIndex); } public String coordinatesToString() { return String.format("x: %8s, y: %8s, z: %8s", String.format("%.3f", x), String.format("%.3f", y), String.format("%.3f", z)); } public SharedInventoryContent getSharedInventoryContent() { return _sharedInventoryContent; } public BlockType getAimedBlockType() { return BlockManager.getInstance().getBlockType(_aimedBlockType); } public void toggleFlying() { _flying = !_flying; } public void spreadLight() { if (_aimedBlockType != 0) { System.out.println("Spread Light! (" + _aimedAdjacentBlockPosition.x() + ", " + _aimedAdjacentBlockPosition.y() + ", " + _aimedAdjacentBlockPosition.z() + ")"); Chunk chunk = Game.getInstance().getWorld().getChunkManager().getChunkContaining( _aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), false, false, false); chunk.spreadLight(_aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), (byte) 15, Chunk.LightType.BLOCK); } } public void unspreadLight() { if (_aimedBlockType != 0) { System.out.println("Spread Light! (" + _aimedAdjacentBlockPosition.x() + ", " + _aimedAdjacentBlockPosition.y() + ", " + _aimedAdjacentBlockPosition.z() + ")"); Chunk chunk = Game.getInstance().getWorld().getChunkManager().getChunkContaining( _aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), false, false, false); chunk.unspreadLight(_aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), (byte) 15, Chunk.LightType.BLOCK); } } public void toggleLight() { if (_aimedBlockType != 0) { Chunk chunk = Game.getInstance().getWorld().getChunkManager().getChunkContaining( _aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), false, false, false); byte light = chunk.getLightAbsolute(_aimedAdjacentBlockPosition.x(), _aimedAdjacentBlockPosition.y(), _aimedAdjacentBlockPosition.z(), Chunk.LightType.BLOCK); if (light == 15) { unspreadLight(); } else { spreadLight(); } } } public void save() throws IOException { File file = Game.getInstance().getRelativeFile(Game.FILE_BASE_USER_DATA, "${world}/player.dat"); DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); /* Position */ IOUtilities.writeVec3f(dos, getPosition()); dos.writeInt(_rotationSegment); /* Spawn Point */ IOUtilities.writeVec3f(dos, getSpawnPoint()); /* Inventory */ InventoryIO.writeInventory(getInventory(), dos, 0, getInventory().size()); /* Selected inventory index */ dos.writeByte(_selectedInventoryItemIndex); dos.close(); } public boolean load() throws IOException { File file = Game.getInstance().getRelativeFile(Game.FILE_BASE_USER_DATA, "${world}/player.dat"); if (!file.exists()) { return false; } DataInputStream dis = new DataInputStream(new FileInputStream(file)); /* Position */ IOUtilities.readVec3f(dis, getPosition()); x = getPosition().x(); y = getPosition().y(); z = getPosition().z(); /* Rotation */ _rotationSegment = dis.readInt(); rotY = _rotationSegment / 10.0f * MathHelper.f_PI; /* Spawn Point */ IOUtilities.readVec3f(dis, getSpawnPoint()); /* Inventory */ InventoryIO.readInventory(dis, getInventory(), 0); /* Selected inventory index */ _selectedInventoryItemIndex = dis.readByte(); setSelectedInventoryItemIndex(_selectedInventoryItemIndex); dis.close(); return true; } public void smash() { if (_aimedBlockType != 0) { _destroying = true; float toolDamage = 0.0f; if (_selectedItem != null) { _body.enableUsingRightHand(); toolDamage = _selectedItem.calcDamageInflictedByBlock(_aimedBlockType); _aimedBlockHealth -= _selectedItem.calcDamageFactorToBlock(_aimedBlockType) * Game.getInstance().getStep() * 5.0f; } else { _aimedBlockHealth -= Game.getInstance().getStep() * 5.0f; } boolean destroy = _aimedBlockHealth <= 0.0f; if (destroy) { _chunkManager.removeBlock(_aimedBlockPosition.x(), _aimedBlockPosition.y(), _aimedBlockPosition.z()); /* Add block to the inventory */ int mineResult = getAimedBlockType().getMineResult(); int mineResultCount = getAimedBlockType().getMineResultCount(); if (mineResult != 0) { for (int i = 0; i < mineResultCount; ++i) { boolean added = _inventory.addToInventory(ItemManager.getInstance().getInventoryItem( (short) (mineResult == -1 ? getAimedBlockType().getInventoryTypeID() : mineResult))); if (added) { SoundManager.getInstance().playSound("pickup"); } } } _aimedBlockPosition.set(0, -1, 0); _aimedAdjacentBlockPosition = null; _aimedBlockType = 0; if (_selectedItem != null) { _selectedItem.inflictDamage(toolDamage); } } } else if (_selectedItem != null) { if (!_airSmashed && !_destroying) { _body.airSmash(); _airSmashed = true; } } } public void buildOrAction() { if (_aimedBlockPosition.y() != -1) { if (getAimedBlockType().hasSpecialAction()) { Block block = _chunkManager.getSpecialBlock(_aimedBlockPosition.x(), _aimedBlockPosition.y(), _aimedAdjacentBlockPosition.z()); block.performSpecialAction(); } else if (_selectedItem instanceof BlockType) { int bX = _aimedAdjacentBlockPosition.x(); int bY = _aimedAdjacentBlockPosition.y(); int bZ = _aimedAdjacentBlockPosition.z(); int pX = MathHelper.floor(x); int pY = MathHelper.floor(y); int pZ = MathHelper.floor(z); if (bX == pX && (bY == pY || bY == pY + 1) && bZ == pZ) { // Player is where the block has to come } else { Chunk bc = _chunkManager.getChunkContaining(bX, bY, bZ, true, true, true); byte currentBlock = bc.getBlockTypeAbsolute(bX, bY, bZ, false, false, false); if (currentBlock == 0) { BlockType type = ((BlockType) _selectedItem); if (type.getCustomClass() == null) { bc.setDefaultBlockAbsolute(bX, bY, bZ, type, (byte) 0, true, true, true); } else { Block block = BlockConstructor.construct(bX, bY, bZ, bc, type.getID(), (byte) 0); System.out.println("Build special block! " + block + " (" + block.getClass().getSimpleName() + ")"); bc.setSpecialBlockAbsolute(bX, bY, bZ, block, true, true, true); } _inventory.getInventoryPlace(_selectedInventoryItemIndex).getStack().decreaseItemCount(); setSelectedInventoryItemIndex(_selectedInventoryItemIndex); } } } } } public void scrollInventoryItem() { float wheel = Mouse.getEventDWheel(); if (wheel != 0) { wheel /= 210.0f; setSelectedInventoryItemIndex( MathHelper.clamp(_selectedInventoryItemIndex + MathHelper.round(wheel), 0, 8)); } } public float getEyeHeight() { return eyeHeight; } public int getSelectedInventoryItemIndex() { return _selectedInventoryItemIndex; } }