Added GUI components for gun ammo, and rifle sound.

This commit is contained in:
Andrew Lalis 2022-07-20 18:33:58 +02:00
parent b88150fd3e
commit 219ccd94db
21 changed files with 589 additions and 50 deletions

View File

@ -8,6 +8,8 @@ import nl.andrewl.aos2_client.control.PlayerViewCursorCallback;
import nl.andrewl.aos2_client.model.ClientPlayer;
import nl.andrewl.aos2_client.model.OtherPlayer;
import nl.andrewl.aos2_client.render.GameRenderer;
import nl.andrewl.aos2_client.sound.SoundManager;
import nl.andrewl.aos2_client.sound.SoundSource;
import nl.andrewl.aos_core.config.Config;
import nl.andrewl.aos_core.model.Player;
import nl.andrewl.aos_core.model.Team;
@ -34,6 +36,8 @@ public class Client implements Runnable {
private final CommunicationHandler communicationHandler;
private final InputHandler inputHandler;
private GameRenderer gameRenderer;
private SoundManager soundManager;
private SoundSource playerSource;
private long lastPlayerUpdate = 0;
private ClientWorld world;
@ -81,6 +85,9 @@ public class Client implements Runnable {
new PlayerInputKeyCallback(inputHandler),
new PlayerInputMouseClickCallback(inputHandler)
);
soundManager = new SoundManager();
soundManager.load("rifle", "sound/m1garand-shot1.wav");
playerSource = new SoundSource();
long lastFrameAt = System.currentTimeMillis();
while (!gameRenderer.windowShouldClose()) {
@ -113,6 +120,7 @@ public class Client implements Runnable {
if (gameRenderer != null) {
gameRenderer.getCamera().setToPlayer(myPlayer);
}
soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity());
lastPlayerUpdate = playerUpdate.timestamp();
} else {
OtherPlayer p = players.get(playerUpdate.clientId());
@ -141,6 +149,8 @@ public class Client implements Runnable {
players.put(op.getId(), op);
} else if (msg instanceof PlayerLeaveMessage leaveMessage) {
players.remove(leaveMessage.id());
} else if (msg instanceof SoundMessage soundMessage) {
playerSource.play(soundManager.getSoundBuffer(soundMessage.name()));
}
}

View File

@ -39,6 +39,7 @@ public class InputHandler {
glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS,
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS,
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS,
glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS,
selectedInventoryIndex
);
if (!currentInputState.equals(lastInputState)) {

View File

@ -8,6 +8,8 @@ import nl.andrewl.aos2_client.render.gui.GUIRenderer;
import nl.andrewl.aos2_client.render.gui.GUITexture;
import nl.andrewl.aos2_client.render.model.Model;
import nl.andrewl.aos_core.model.item.BlockItemStack;
import nl.andrewl.aos_core.model.item.Gun;
import nl.andrewl.aos_core.model.item.GunItemStack;
import nl.andrewl.aos_core.model.item.ItemTypes;
import org.joml.Matrix4f;
import org.joml.Vector3f;
@ -44,6 +46,11 @@ public class GameRenderer {
private Model rifleModel;
private Model blockModel;
// Standard GUI textures.
private GUITexture crosshairTexture;
private GUITexture clipTexture;
private GUITexture bulletTexture;
private long windowHandle;
private int screenWidth = 800;
private int screenHeight = 600;
@ -110,17 +117,16 @@ public class GameRenderer {
this.chunkRenderer = new ChunkRenderer();
log.debug("Initialized chunk renderer.");
this.guiRenderer = new GUIRenderer();
// TODO: More organized way to load textures for GUI.
try {
var crosshairTexture = new GUITexture("gui/crosshair.png");
float size = 32;
crosshairTexture.getScale().set(size / screenWidth, size / screenHeight);
guiRenderer.addTexture(crosshairTexture);
} catch (IOException e) {
throw new RuntimeException(e);
}
crosshairTexture = new GUITexture("gui/crosshair.png");
clipTexture = new GUITexture("gui/clip.png");
bulletTexture = new GUITexture("gui/bullet.png");
guiRenderer.addTexture("crosshair", crosshairTexture);
guiRenderer.addTexture("clip", clipTexture);
guiRenderer.addTexture("bullet", bulletTexture);
log.debug("Initialized GUI renderer.");
this.modelRenderer = new ModelRenderer();
try {
playerModel = new Model("model/player_simple.obj", "model/simple_player.png");
@ -212,7 +218,32 @@ public class GameRenderer {
modelRenderer.end();
guiRenderer.draw();
// GUI rendering
guiRenderer.start();
guiRenderer.draw(crosshairTexture, crosshairTexture.getIdealScaleX(32, screenWidth), crosshairTexture.getIdealScaleY(32, screenHeight), 0, 0);
// If we're holding a gun, draw clip and bullet graphics.
if (client.getMyPlayer().getInventory().getSelectedItemStack().getType() instanceof Gun) {
GunItemStack stack = (GunItemStack) client.getMyPlayer().getInventory().getSelectedItemStack();
for (int i = 0; i < stack.getClipCount(); i++) {
guiRenderer.draw(
clipTexture,
clipTexture.getIdealScaleX(64, screenWidth),
clipTexture.getIdealScaleY(clipTexture.getIdealHeight(64), screenHeight),
0.90f,
-0.90f + (i * 0.15f)
);
}
for (int i = 0; i < stack.getBulletCount(); i++) {
guiRenderer.draw(
bulletTexture,
bulletTexture.getIdealScaleX(16, screenWidth),
bulletTexture.getIdealScaleY(bulletTexture.getIdealHeight(16), screenHeight),
0.80f - (i * 0.05f),
-0.90f
);
}
}
guiRenderer.end();
glfwSwapBuffers(windowHandle);
glfwPollEvents();

View File

@ -5,8 +5,8 @@ import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import static org.lwjgl.opengl.GL46.*;
@ -19,9 +19,10 @@ public class GUIRenderer {
private final int vertexCount;
private final ShaderProgram shaderProgram;
private final int transformUniformLocation;
private final Matrix4f transformMatrix;
private final float[] transformMatrixData;
private final List<GUITexture> guiTextures = new ArrayList<>();
private final Map<String, GUITexture> textures = new HashMap<>();
public GUIRenderer() {
vaoId = glGenVertexArrays();
@ -47,38 +48,52 @@ public class GUIRenderer {
.build();
transformUniformLocation = shaderProgram.getUniform("transform");
shaderProgram.bindAttribute(0, "position");
this.transformMatrix = new Matrix4f();
this.transformMatrixData = new float[16];
}
public void addTexture(GUITexture texture) {
guiTextures.add(texture);
public void loadTexture(String name, String resource) {
textures.put(name, new GUITexture(resource));
}
public void draw() {
public void addTexture(String name, GUITexture texture) {
textures.put(name, texture);
}
public void start() {
shaderProgram.use();
glBindVertexArray(vaoId);
glEnableVertexAttribArray(0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
for (var texture : guiTextures) {
glActiveTexture(GL_TEXTURE0);
Matrix4f transform = new Matrix4f()
.translate(texture.getPosition().x, texture.getPosition().y, 0)
.scale(texture.getScale().x, texture.getScale().y, 1);
float[] transformData = new float[16];
transform.get(transformData);
glUniformMatrix4fv(transformUniformLocation, false, transformData);
glBindTexture(GL_TEXTURE_2D, texture.getTextureId());
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
}
}
public void draw(String name, float scaleX, float scaleY, float x, float y) {
draw(textures.get(name), scaleX, scaleY, x, y);
}
public void draw(GUITexture texture, float scaleX, float scaleY, float x, float y) {
glActiveTexture(0);
transformMatrix.identity()
.translate(x, y, 0)
.scale(scaleX, scaleY, 1)
.get(transformMatrixData);
glUniformMatrix4fv(transformUniformLocation, false, transformMatrixData);
glBindTexture(GL_TEXTURE_2D, texture.getTextureId());
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
}
public void end() {
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
shaderProgram.stopUsing();
}
public void free() {
for (var tex : guiTextures) tex.free();
for (var tex : textures.values()) tex.free();
glDeleteBuffers(vboId);
glDeleteVertexArrays(vaoId);
shaderProgram.free();

View File

@ -2,7 +2,6 @@ package nl.andrewl.aos2_client.render.gui;
import nl.andrewl.aos_core.ImageUtils;
import org.joml.Vector2f;
import org.joml.Vector3f;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@ -10,14 +9,21 @@ import java.io.IOException;
import static org.lwjgl.opengl.GL46.*;
/**
* Represents a texture loaded onto the GPU.
*/
public class GUITexture {
private final int textureId;
/**
* The original image's width.
*/
private final int width;
/**
* The original image's height.
*/
private final int height;
private final Vector2f position = new Vector2f(0, 0);
private final Vector2f scale = new Vector2f(1, 1);
public GUITexture(String location) throws IOException {
public GUITexture(String location) {
try (var in = GUITexture.class.getClassLoader().getResourceAsStream(location)) {
if (in == null) throw new IOException("Couldn't load texture image from " + location);
BufferedImage img = ImageIO.read(in);
@ -31,21 +37,43 @@ public class GUITexture {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
var buf = ImageUtils.decodePng(img);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
} catch (IOException e) {
throw new RuntimeException("Failed to load GUI texture.", e);
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public float getAspectRatio() {
return (float) width / height;
}
public float getIdealHeight(float width) {
return width / getAspectRatio();
}
public float getIdealWidth(float height) {
return height * getAspectRatio();
}
public float getIdealScaleX(float desiredWidth, float screenWidth) {
return desiredWidth / screenWidth;
}
public float getIdealScaleY(float desiredHeight, float screenHeight) {
return desiredHeight / screenHeight;
}
public int getTextureId() {
return textureId;
}
public Vector2f getPosition() {
return position;
}
public Vector2f getScale() {
return scale;
}
public void free() {
glDeleteTextures(textureId);
}

View File

@ -0,0 +1,53 @@
package nl.andrewl.aos2_client.sound;
import nl.andrewl.aos2_client.model.ClientPlayer;
import org.joml.Vector3f;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.ALC;
import org.lwjgl.openal.ALCCapabilities;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import static org.lwjgl.openal.ALC10.*;
import static org.lwjgl.openal.AL10.*;
public class SoundManager {
private final long alContext;
private final Map<String, Integer> audioBuffers = new HashMap<>();
public SoundManager() {
long device = alcOpenDevice((ByteBuffer) null);
ALCCapabilities capabilities = ALC.createCapabilities(device);
alContext = alcCreateContext(device, (IntBuffer) null);
alcMakeContextCurrent(alContext);
AL.createCapabilities(capabilities);
}
public void load(String name, String resource) {
int bufferId = alGenBuffers();
audioBuffers.put(name, bufferId);
WaveData waveData = WaveData.create(resource);
alBufferData(bufferId, waveData.format, waveData.data, waveData.samplerate);
waveData.dispose();
}
public void updateListener(Vector3f position, Vector3f velocity) {
alListener3f(AL_POSITION, position.x(), position.y(), position.z());
alListener3f(AL_VELOCITY, velocity.x(), velocity.y(), velocity.z());
}
public int getSoundBuffer(String name) {
return audioBuffers.get(name);
}
public void free() {
for (var bufferId : audioBuffers.values()) {
alDeleteBuffers(bufferId);
}
alcDestroyContext(alContext);
}
}

View File

@ -0,0 +1,23 @@
package nl.andrewl.aos2_client.sound;
import static org.lwjgl.openal.AL10.*;
public class SoundSource {
private final int sourceId;
public SoundSource() {
sourceId = alGenSources();
alSourcef(sourceId, AL_GAIN, 1);
alSourcef(sourceId, AL_PITCH, 1);
alSource3f(sourceId, AL_POSITION, 0, 0, 0);
}
public void play(int bufferId) {
alSourcei(sourceId, AL_BUFFER, bufferId);
alSourcePlay(sourceId);
}
public void free() {
alDeleteSources(sourceId);
}
}

View File

@ -0,0 +1,86 @@
package nl.andrewl.aos2_client.sound;
import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL10;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
public class WaveData {
final int format;
final int samplerate;
final int totalBytes;
final int bytesPerFrame;
final ByteBuffer data;
private final AudioInputStream audioStream;
private final byte[] dataArray;
private WaveData(AudioInputStream stream) {
this.audioStream = stream;
AudioFormat audioFormat = stream.getFormat();
format = getOpenAlFormat(audioFormat.getChannels(), audioFormat.getSampleSizeInBits());
this.samplerate = (int) audioFormat.getSampleRate();
this.bytesPerFrame = audioFormat.getFrameSize();
this.totalBytes = (int) (stream.getFrameLength() * bytesPerFrame);
this.data = BufferUtils.createByteBuffer(totalBytes);
this.dataArray = new byte[totalBytes];
loadData();
}
protected void dispose() {
try {
audioStream.close();
data.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
private ByteBuffer loadData() {
try {
int bytesRead = audioStream.read(dataArray, 0, totalBytes);
data.clear();
data.put(dataArray, 0, bytesRead);
data.flip();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Couldn't read bytes from audio stream!");
}
return data;
}
public static WaveData create(String file){
InputStream stream = WaveData.class.getClassLoader().getResourceAsStream(file);
if (stream == null) {
System.err.println("Couldn't find file: " + file);
return null;
}
InputStream bufferedInput = new BufferedInputStream(stream);
AudioInputStream audioStream;
try {
audioStream = AudioSystem.getAudioInputStream(bufferedInput);
} catch (UnsupportedAudioFileException | IOException e) {
throw new RuntimeException(e);
}
return new WaveData(audioStream);
}
private static int getOpenAlFormat(int channels, int bitsPerSample) {
if (channels == 1) {
return bitsPerSample == 8 ? AL10.AL_FORMAT_MONO8 : AL10.AL_FORMAT_MONO16;
} else {
return bitsPerSample == 8 ? AL10.AL_FORMAT_STEREO8 : AL10.AL_FORMAT_STEREO16;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

View File

@ -42,6 +42,7 @@ public final class Net {
serializer.registerTypeSerializer(13, new InventorySerializer());
serializer.registerTypeSerializer(14, new ItemStackSerializer());
serializer.registerType(15, InventorySelectedStackMessage.class);
serializer.registerType(16, SoundMessage.class);
}
public static ExtendedDataInputStream getInputStream(InputStream in) {

View File

@ -14,6 +14,7 @@ public class Gun extends Item {
private final float reloadTime;
private final float baseDamage;
private final float recoil;
private boolean automatic;
public Gun(
int id,
@ -25,7 +26,8 @@ public class Gun extends Item {
float shotCooldownTime,
float reloadTime,
float baseDamage,
float recoil
float recoil,
boolean automatic
) {
super(id, name, 1);
this.maxClipCount = maxClipCount;
@ -36,6 +38,7 @@ public class Gun extends Item {
this.reloadTime = reloadTime;
this.baseDamage = baseDamage;
this.recoil = recoil;
this.automatic = automatic;
}
public int getMaxClipCount() {
@ -69,4 +72,8 @@ public class Gun extends Item {
public float getRecoil() {
return recoil;
}
public boolean isAutomatic() {
return automatic;
}
}

View File

@ -14,7 +14,8 @@ public class Rifle extends Gun {
0.8f,
2.5f,
80f,
50f
50f,
false
);
}
}

View File

@ -20,5 +20,6 @@ public record ClientInputState(
// Interaction
boolean hitting, // Usually a "left-click" action.
boolean interacting, // Usually a "right-click" action.
boolean reloading, // Usually the "R" key.
int selectedInventoryIndex // The selected index in the player's inventory.
) implements Message {}

View File

@ -0,0 +1,12 @@
package nl.andrewl.aos_core.net.client;
import nl.andrewl.record_net.Message;
/**
* A message that indicates that a sound has been emitted somewhere in the
* world, and that clients may need to play the sound.
*/
public record SoundMessage(
String name,
float px, float py, float pz
) implements Message {}

BIN
design/bullet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

79
design/bullet.svg Normal file
View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="bullet.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="1025.0529"
inkscape:cy="684.95586"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1049"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g827"
transform="translate(7.4821434,33.262203)"
inkscape:export-xdpi="20.76"
inkscape:export-ydpi="20.76">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path821"
d="m 60.476187,95.538688 c 0,-54.617567 37.041667,-82.587796 37.041667,-82.587796 0,0 37.041666,27.970235 37.041666,82.587796"
style="fill:#c48916;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
ry="2.6458333"
rx="2.6458333"
y="95.538689"
x="60.476189"
height="119.86935"
width="74.083336"
id="rect815"
style="fill:#383736;fill-opacity:1;stroke:#7c7c7c;stroke-width:4.23333311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
design/clip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

135
design/clip.svg Normal file
View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="clip.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="379.84557"
inkscape:cy="674.60205"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1049"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#383736;fill-opacity:1;stroke:#7c7c7c;stroke-width:8.46666622;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect867"
width="156.86011"
height="110.36904"
x="20.788689"
y="116.32738"
rx="2.6458333"
ry="2.6458333"
inkscape:export-xdpi="19.67"
inkscape:export-ydpi="19.67" />
<g
id="g873"
transform="translate(7.5595238,-2.2339507)"
inkscape:export-xdpi="19.67"
inkscape:export-ydpi="19.67">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path821"
d="m 64.335157,155.54667 c 10.862887,-10.86289 23.793101,-9.05867 23.793101,-9.05867 0,0 1.804217,12.93021 -9.058669,23.7931"
style="fill:#c48916;fill-opacity:1;stroke:none;stroke-width:4.23333311;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
transform="rotate(45)"
ry="0.74420118"
rx="0.74420118"
y="64.496277"
x="155.47993"
height="33.716"
width="20.837635"
id="rect815"
style="fill:#b63417;fill-opacity:1;stroke:#7c7c7c;stroke-width:4.23333311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g879"
transform="translate(33.450892,-2.2339507)"
inkscape:export-xdpi="19.67"
inkscape:export-ydpi="19.67">
<path
style="fill:#c48916;fill-opacity:1;stroke:none;stroke-width:4.23333311;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 64.335157,155.54667 c 10.862887,-10.86289 23.793101,-9.05867 23.793101,-9.05867 0,0 1.804217,12.93021 -9.058669,23.7931"
id="path875"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="fill:#b63417;fill-opacity:1;stroke:#7c7c7c;stroke-width:4.23333311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect877"
width="20.837635"
height="33.716"
x="155.47993"
y="64.496277"
rx="0.74420118"
ry="0.74420118"
transform="rotate(45)" />
</g>
<g
transform="translate(59.342262,-2.2339507)"
id="g885"
inkscape:export-xdpi="19.67"
inkscape:export-ydpi="19.67">
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path881"
d="m 64.335157,155.54667 c 10.862887,-10.86289 23.793101,-9.05867 23.793101,-9.05867 0,0 1.804217,12.93021 -9.058669,23.7931"
style="fill:#c48916;fill-opacity:1;stroke:none;stroke-width:4.23333311;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
transform="rotate(45)"
ry="0.74420118"
rx="0.74420118"
y="64.496277"
x="155.47993"
height="33.716"
width="20.837635"
id="rect883"
style="fill:#b63417;fill-opacity:1;stroke:#7c7c7c;stroke-width:4.23333311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -3,12 +3,12 @@ package nl.andrewl.aos2_server.logic;
import nl.andrewl.aos2_server.Server;
import nl.andrewl.aos2_server.ServerPlayer;
import nl.andrewl.aos2_server.config.ServerConfig;
import nl.andrewl.aos_core.model.item.BlockItemStack;
import nl.andrewl.aos_core.model.item.ItemTypes;
import nl.andrewl.aos_core.model.item.*;
import nl.andrewl.aos_core.model.world.World;
import nl.andrewl.aos_core.net.client.ClientInputState;
import nl.andrewl.aos_core.net.client.InventorySelectedStackMessage;
import nl.andrewl.aos_core.net.client.ItemStackMessage;
import nl.andrewl.aos_core.net.client.SoundMessage;
import nl.andrewl.aos_core.net.world.ChunkUpdateMessage;
import org.joml.Math;
import org.joml.Vector2i;
@ -30,6 +30,11 @@ public class PlayerActionManager {
private long lastBlockRemovedAt = 0;
private long lastBlockPlacedAt = 0;
private long gunLastShotAt = 0;
private boolean gunNeedsReCock = false;
private boolean gunReloading = false;
private long gunReloadingStartedAt = 0;
private boolean updated = false;
public PlayerActionManager(ServerPlayer player) {
@ -38,7 +43,7 @@ public class PlayerActionManager {
player.getId(),
false, false, false, false,
false, false, false,
false, false,
false, false, false,
player.getInventory().getSelectedIndex()
);
}
@ -67,8 +72,15 @@ public class PlayerActionManager {
updated = true; // Tell everyone else that this player's selected item has changed.
}
if (player.getInventory().getSelectedItemStack().getType().equals(ItemTypes.BLOCK)) {
tickBlockAction(now, server, world);
ItemStack selectedStack = player.getInventory().getSelectedItemStack();
if (selectedStack instanceof BlockItemStack b) {
tickBlockAction(now, server, world, b);
} else if (selectedStack instanceof GunItemStack g) {
try {
tickGunAction(now, server, world, g);
} catch (Exception e) {
System.out.println("bleh");
}
}
if (player.isCrouching() != lastInputState.crouching()) {
@ -79,8 +91,52 @@ public class PlayerActionManager {
tickMovement(dt, world, server.getConfig().physics);
}
private void tickBlockAction(long now, Server server, World world) {
BlockItemStack stack = (BlockItemStack) player.getInventory().getSelectedItemStack();
private void tickGunAction(long now, Server server, World world, GunItemStack g) {
Gun gun = (Gun) g.getType();
if (// Check to see if the player is shooting.
lastInputState.hitting() &&
g.getBulletCount() > 0 &&
!gunReloading &&
now - gunLastShotAt > gun.getShotCooldownTime() * 1000 &&
(gun.isAutomatic() || !gunNeedsReCock)
) {
// TODO: trace a ray from gun to see if players intersect with it.
g.setBulletCount(g.getBulletCount() - 1);
gunLastShotAt = now;
if (!gun.isAutomatic()) {
gunNeedsReCock = true;
}
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
server.getPlayerManager().broadcastUdpMessage(new SoundMessage("rifle", player.getPosition().x(), player.getPosition().y(), player.getPosition().z()));
}
if (// Check to see if the player is reloading.
lastInputState.reloading() &&
!gunReloading &&
g.getClipCount() > 0
) {
g.setClipCount(g.getClipCount() - 1);
gunReloadingStartedAt = now;
gunReloading = true;
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
}
if (// Check to see if reloading is done.
gunReloading &&
now - gunReloadingStartedAt > gun.getReloadTime() * 1000
) {
g.setBulletCount(gun.getMaxBulletCount());
gunReloading = false;
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
}
// Check to see if the player released the trigger, for non-automatic weapons.
if (!gun.isAutomatic() && gunNeedsReCock && !lastInputState.hitting()) {
gunNeedsReCock = false;
}
}
private void tickBlockAction(long now, Server server, World world, BlockItemStack stack) {
// Check for breaking blocks.
if (
lastInputState.hitting() &&