package org.jrenner.fps; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.ui.TextField; import com.badlogic.gdx.scenes.scene2d.utils.Align; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.StringBuilder; import com.badlogic.gdx.utils.TimeUtils; import org.jrenner.fps.entity.Entity; import org.jrenner.fps.net.NetManager; import org.jrenner.fps.net.packages.ChatMessage; public class HUD { Stage stage; Skin skin; Label label; Label chatLabel; float bw = 200f * View.screenSizeRatio(); float bh = 160f * View.screenSizeRatio(); public TextButton forward, back, right, left, shoot; private Table arrowsTable; private TextField chatField; private ShapeRenderer shapes = new ShapeRenderer(); private Array<ChatMessage> chatMessages = new Array<>(); public HUD() { skin = Assets.skin; stage = new Stage(); Table table = new Table(); stage.addActor(table); table.setFillParent(true); table.align(Align.left | Align.top); table.setSkin(skin); label = new Label("", skin); table.add(label).row(); Table chatTable = new Table(); chatTable.align(Align.left | Align.bottom); chatTable.setSize(View.width(), View.height() / 2f); stage.addActor(chatTable); chatLabel = new Label("", Assets.chatLabelStyle); chatLabel.setFillParent(true); chatLabel.setAlignment(Align.left | Align.bottom); chatTable.add(chatLabel); // button interface for mobile if (Main.isMobile()) { Table btnTable = new Table(); table.add(btnTable).left().top(); TextButton camBtn = new TextButton("Camera", skin); camBtn.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { Toggleable.toggle(Toggleable.FREE_CAMERA); } }); btnTable.add(camBtn).size(bw, bh); TextButton controlBtn = new TextButton("Control", skin); controlBtn.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { Toggleable.toggle(Toggleable.CONTROL_PLAYER); } }); btnTable.add(controlBtn).size(bw, bh); arrowsTable = new Table(); arrowsTable.setFillParent(true); stage.addActor(arrowsTable); arrowsTable.align(Align.right | Align.bottom); float sz = bw; float delta = sz; float baseX = (View.width() - delta) - 10f; float baseY = 10f; float dX = 0f; float dY = 0f; forward = new TextButton("For", skin); forward.setSize(sz, sz); forward.setPosition(baseX - delta, baseY + delta); right = new TextButton("Right", skin); right.setSize(sz, sz); right.setPosition(baseX + dX, baseY + dY); dX -= delta; back = new TextButton("Back", skin); back.setSize(sz, sz); back.setPosition(baseX + dX, baseY + dY); dX -= delta; left = new TextButton("Left", skin); left.setSize(sz, sz); left.setPosition(baseX + dX, baseY + dY); dX -= delta * 2f; TextButton jumpBtn = new TextButton("Jump", skin); jumpBtn.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { if (Main.inst.client.player != null) { Main.inst.client.player.entity.jump(); } } }); jumpBtn.setSize(sz, sz); jumpBtn.setPosition(baseX + dX, baseY + dY); dX -= delta * 2; shoot = new TextButton("Shoot", skin); shoot.setPosition(baseX + dX, baseY + dY); shoot.setSize(sz, sz); stage.addActor(forward); stage.addActor(right); stage.addActor(back); stage.addActor(left); stage.addActor(jumpBtn); stage.addActor(shoot); } SpriteDrawable crossDraw = new SpriteDrawable(Crosshair.create()); Image crosshair = new Image(crossDraw); crosshair.setPosition(View.width() / 2, View.height() / 2, Align.center); stage.addActor(crosshair); createChatField(); } private StringBuilder sb = new StringBuilder(); /** output extra debug info to the in-game HUD */ boolean debugHUD = true; public void update() { if (Main.frame % 15 != 0) return; Player player = Main.inst.client.player; sb.delete(0, sb.length); sb.append("FPS: ").append(Integer.toString(Gdx.graphics.getFramesPerSecond())); if (Main.serverType != null) { sb.append("\nServerType: ").append(Main.serverType); } if (Main.inst.client != null) { sb.append("\nClientType: ").append(Main.inst.client.getClass().getSimpleName()); } if (!Main.isLocalServer()) { sb.append("\nServer Address: ").append(NetManager.host); } if (!Main.isLocalServer()) { sb.append("\nPing: ").append(Integer.toString(Main.getNetClient().ping)); } String camera = Toggleable.freeCamera() ? "Free" : "Player"; String control = Toggleable.controlPlayer() ? "Player" : "Camera"; String mouse = Toggleable.mouseLook() ? "Yes" : "No"; sb.append("\nCamera: ").append(camera); sb.append("\nUser Control: ").append(control); sb.append("\nMouse Look: ").append(mouse); if (player != null && debugHUD) { sb.append("\nVelocity: ").append(Tools.fmt(player.entity.getVelocity(), 1)); sb.append("\nPosition: ").append(Tools.fmt(player.entity.getPosition(), 1)); if (player.entity.interpolator != null) { sb.append("\nPosition Error: ").append(Tools.fmt(player.entity.interpolator.posError, 2)); } sb.append("\nOn Ground: ").append(player.entity.onGround); sb.append("\nYaw: ").append(player.entity.getYaw()); sb.append("\nPitch: ").append(player.entity.getPitch()); sb.append("\n\nPhysics time: ").append(String.format("%4s", Main.physicsTime)); sb.append("\nGround dist: ").append(String.format("%.4f", player.entity.distFromGround)); sb.append("\nGround normal: ").append(Tools.fmt(Physics.inst.getFloorNormal(player.entity.getPosition()))); sb.append("\nGroundPiece visibility: ").append(View.visibleGroundPieces).append(" / ").append(LevelBuilder.groundPieces.size); sb.append("\nEntity visibility: ").append(View.visibleEntities).append(" / ").append(Entity.list.size); } sb.append("\nPress T to chat\nPress R to respawn"); label.setText(sb.toString()); sb.delete(0, sb.length); showChatMessages(sb); chatLabel.setText(sb.toString()); if (queueOpenChat) { showChatFieldForRealIMeanIt(); } } public void draw() { update(); stage.act(); stage.draw(); shapes.end(); } public void addChatMessage(ChatMessage chatMsg) { System.out.println("add chat message: " + chatMsg); chatMessages.add(chatMsg); } private static final long chatMessageLifeTime = 5000; // ms public void showChatMessages(StringBuilder sb) { for (ChatMessage chat : chatMessages) { long elapsed = TimeUtils.timeSinceMillis(chat.createTime); if (elapsed >= chatMessageLifeTime) { chatMessages.removeValue(chat, true); } else { sb.append("\n["); if (chat.playerId == -1) { sb.append("Server"); } else { sb.append("Player ").append(chat.playerId); } sb.append("]: ").append(chat.text); } } } public boolean isChatOpen() { return stage.getKeyboardFocus() == chatField; } boolean queueOpenChat = false; // must be queued to stop the 't' from being entered into the field. Stupid. find a better way public void showChatField() { queueOpenChat = true; } private void showChatFieldForRealIMeanIt() { chatField.setVisible(true); stage.setKeyboardFocus(chatField); queueOpenChat = false; } public void hideChatField() { chatField.setVisible(false); stage.unfocus(chatField); clearChatField(); } public void clearChatField() { chatField.setText(""); } private void createChatField() { TextField.TextFieldStyle chatTFStyle = new TextField.TextFieldStyle(skin.get(TextField.TextFieldStyle.class)); chatTFStyle.background = skin.getDrawable("button-up"); chatField = new TextField("", chatTFStyle); chatField.setMaxLength(256); chatField.setPosition(0f, chatField.getHeight()); stage.addActor(chatField); hideChatField(); final TextField cf = chatField; chatField.setTextFieldListener(new TextField.TextFieldListener() { @Override public void keyTyped(TextField textField, char c) { if (c == '\r' || c == '\n') { ChatMessage chatMsg = new ChatMessage(); chatMsg.text = cf.getText(); Main.inst.client.sendChatMessage(chatMsg); clearChatField(); hideChatField(); } } }); chatField.setTextFieldFilter(new TextField.TextFieldFilter() { @Override public boolean acceptChar(TextField textField, char c) { if (isChatOpen()) { return true; } return false; } }); } public void getMoveTranslation(Vector3 trans) { trans.setZero(); float x = 0f, y = 0f, z = 0f; if (forward.isPressed()) { z += 1f; } if (back.isPressed()) { z -= 1f; } if (left.isPressed()) { x += 1f; } if (right.isPressed()) { x -= 1f; } trans.set(x, y, z); } }