Implemented damage types and void below world

This commit is contained in:
jglrxavpok 2020-04-27 20:33:08 +02:00
parent 26debd0d4b
commit 6b77627d60
13 changed files with 205 additions and 16 deletions

View File

@ -2,6 +2,7 @@ package fr.themode.demo;
import fr.themode.demo.blocks.StoneBlock;
import fr.themode.demo.blocks.UpdatableBlockDemo;
import fr.themode.demo.commands.GamemodeCommand;
import fr.themode.demo.commands.HealthCommand;
import fr.themode.demo.commands.SimpleCommand;
import net.minestom.server.MinecraftServer;
@ -32,6 +33,7 @@ public class Main {
CommandManager commandManager = MinecraftServer.getCommandManager();
commandManager.register(new HealthCommand());
commandManager.register(new SimpleCommand());
commandManager.register(new GamemodeCommand());
RecipeManager recipeManager = MinecraftServer.getRecipeManager();
ShapelessRecipe shapelessRecipe = new ShapelessRecipe("test", "groupname") {

View File

@ -7,6 +7,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.benchmark.BenchmarkManager;
import net.minestom.server.benchmark.ThreadResult;
import net.minestom.server.entity.*;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.*;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.inventory.Inventory;
@ -97,7 +98,7 @@ public class PlayerInit {
Entity entity = event.getTarget();
if (entity instanceof EntityCreature) {
EntityCreature creature = (EntityCreature) entity;
creature.damage(-1);
creature.damage(DamageType.fromPlayer(player), -1);
Vector velocity = player.getPosition().clone().getDirection().multiply(6);
velocity.setY(4f);
entity.setVelocity(velocity, 150);
@ -107,7 +108,7 @@ public class PlayerInit {
Vector velocity = player.getPosition().clone().getDirection().multiply(4);
velocity.setY(3.5f);
target.setVelocity(velocity, 150);
target.damage(5);
target.damage(DamageType.fromPlayer(player), 5);
player.sendMessage("ATTACK");
}
});

View File

@ -0,0 +1,73 @@
package fr.themode.demo.commands;
import fr.themode.command.Arguments;
import fr.themode.command.Command;
import fr.themode.command.arguments.Argument;
import fr.themode.command.arguments.ArgumentType;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import java.util.Optional;
/**
* Command that make a player change gamemode
*/
public class GamemodeCommand extends Command<Player> {
public GamemodeCommand() {
super("gamemode", "g", "gm");
setCondition(this::isAllowed);
setDefaultExecutor(this::usage);
Argument player = ArgumentType.String("player");
GameMode[] gameModes = GameMode.values();
String[] names = new String[gameModes.length];
for (int i = 0; i < gameModes.length; i++) {
names[i] = gameModes[i].name().toLowerCase();
}
Argument mode = ArgumentType.Word("mode").from(names);
addCallback(this::gameModeCallback, mode);
addSyntax(this::executeOnSelf, mode);
addSyntax(this::executeOnOther, player, mode);
}
private void usage(Player player, Arguments arguments) {
player.sendMessage("Usage: /gamemode [player] <gamemode>");
}
private void executeOnSelf(Player player, Arguments arguments) {
String gamemodeName = arguments.getWord("mode");
GameMode mode = GameMode.valueOf(gamemodeName.toUpperCase());
assert mode != null; // mode is not supposed to be null, because gamemodeName will be valid
player.setGameMode(mode);
player.sendMessage("You are now playing in "+gamemodeName);
System.out.println("hello");
}
private void executeOnOther(Player player, Arguments arguments) {
String gamemodeName = arguments.getWord("mode");
String targetName = arguments.getString("player");
GameMode mode = GameMode.valueOf(gamemodeName.toUpperCase());
assert mode != null; // mode is not supposed to be null, because gamemodeName will be valid
Optional<Player> target = player.getInstance().getPlayers().stream().filter(p -> p.getUsername().equals(targetName)).findFirst();
if(target.isPresent()) {
target.get().setGameMode(mode);
target.get().sendMessage("You are now playing in "+gamemodeName);
} else {
player.sendMessage("'"+targetName+"' is not a valid player name.");
}
}
private void gameModeCallback(Player player, String gamemode, int error) {
player.sendMessage("'"+gamemode+"' is not a valid gamemode!");
}
private boolean isAllowed(Player player) {
return true; // TODO: permissions
}
}

View File

@ -28,7 +28,7 @@ public class HealthCommand extends Command<Player> {
}
private boolean condition(Player player) {
// Your custom condition, called no matter the syntax used
// TODO: Your custom condition, called no matter the syntax used
boolean hasPerm = true;
if (!hasPerm) {
player.sendMessage("You do not have permission !");

View File

@ -245,6 +245,7 @@ public abstract class Entity implements Viewable, DataContainer {
if (!isOnGround()) {
float strength = gravityDragPerTick * gravityTickCounter;
boolean foundBlock = false;
int firstBlock = 0;
for (int y = (int) position.getY(); y > 0; y--) {
BlockPosition blockPosition = new BlockPosition(position.getX(), y, position.getZ());
@ -253,6 +254,7 @@ public abstract class Entity implements Viewable, DataContainer {
// System.out.println("id: " + blockId);
if (blockId != 0) {
firstBlock = y;
foundBlock = true;
//System.out.println("first block: " + y);
break;
}
@ -260,7 +262,10 @@ public abstract class Entity implements Viewable, DataContainer {
float newY = position.getY() - strength;
//System.out.println("REFRESH Y " + newY);
newY = Math.max(newY, firstBlock);
// allow entities to go below the world
if(foundBlock) {
newY = Math.max(newY, firstBlock);
}
refreshPosition(position.getX(), newY, position.getZ());
gravityTickCounter++;
if (isOnGround()) { // Round Y axis when gravity movement is done
@ -276,6 +281,8 @@ public abstract class Entity implements Viewable, DataContainer {
}
}
handleVoid();
// Call the abstract update method
update();
@ -291,6 +298,16 @@ public abstract class Entity implements Viewable, DataContainer {
}
}
/**
* How does this entity handle being in the void?
*/
protected void handleVoid() {
// Kill if in void
if(getInstance().isInVoid(this.position)) {
remove();
}
}
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
this.eventCallbacks.put(eventClass, callback);
}

View File

@ -2,13 +2,15 @@ package net.minestom.server.entity;
public enum GameMode {
SURVIVAL((byte) 0), CREATIVE((byte) 1), ADVENTURE((byte) 2), SPECTATOR((byte) 3);
SURVIVAL((byte) 0, true), CREATIVE((byte) 1, false), ADVENTURE((byte) 2, true), SPECTATOR((byte) 3, false);
private byte id;
private boolean hardcore;
private boolean canTakeDamage;
GameMode(byte id) {
GameMode(byte id, boolean canTakeDamage) {
this.id = id;
this.canTakeDamage = canTakeDamage;
}
public void setHardcore(boolean hardcore) {
@ -22,4 +24,8 @@ public enum GameMode {
public boolean isHardcore() {
return hardcore;
}
public boolean canTakeDamage() {
return canTakeDamage;
}
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.entity;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.property.Attribute;
import net.minestom.server.event.DeathEvent;
import net.minestom.server.event.PickupItemEvent;
@ -103,7 +104,10 @@ public abstract class LivingEntity extends Entity {
callEvent(DeathEvent.class, deathEvent);
}
public void damage(float value) {
public void damage(DamageType type, float value) {
if(isImmune(type)) {
return;
}
EntityAnimationPacket entityAnimationPacket = new EntityAnimationPacket();
entityAnimationPacket.entityId = getEntityId();
entityAnimationPacket.animation = EntityAnimationPacket.Animation.TAKE_DAMAGE;
@ -111,6 +115,15 @@ public abstract class LivingEntity extends Entity {
setHealth(getHealth() - value);
}
/**
* Is this entity immune to the given type of damage?
* @param type the type of damage
* @return true iff this entity is immune to the given type of damage
*/
public boolean isImmune(DamageType type) {
return false;
}
public float getHealth() {
return health;
}
@ -195,4 +208,12 @@ public abstract class LivingEntity extends Entity {
setAttribute(attribute, attribute.getDefaultValue());
}
}
@Override
protected void handleVoid() {
// Kill if in void
if(getInstance().isInVoid(this.position)) {
damage(DamageType.VOID, 10f);
}
}
}

View File

@ -6,6 +6,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.bossbar.BossBar;
import net.minestom.server.chat.Chat;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.property.Attribute;
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
import net.minestom.server.event.*;
@ -406,11 +407,11 @@ public class Player extends LivingEntity {
}
@Override
public void damage(float value) {
if (getGameMode() == GameMode.CREATIVE)
return;
super.damage(value);
public boolean isImmune(DamageType type) {
if(getGameMode().canTakeDamage()) {
return type != DamageType.VOID;
}
return super.isImmune(type);
}
@Override
@ -450,6 +451,11 @@ public class Player extends LivingEntity {
return !itemDropEvent.isCancelled();
}
public Position getRespawnPoint() {
// TODO: Custom
return new Position(0f, 70f, 0f);
}
public void respawn() {
if (!isDead())
return;
@ -460,7 +466,7 @@ public class Player extends LivingEntity {
respawnPacket.gameMode = getGameMode();
respawnPacket.levelType = getLevelType();
getPlayerConnection().sendPacket(respawnPacket);
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(getPosition());
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(getRespawnPoint());
callEvent(PlayerRespawnEvent.class, respawnEvent);
refreshIsDead(false);
@ -604,6 +610,14 @@ public class Player extends LivingEntity {
return gameMode;
}
/**
* Returns true iff this player is in creative. Used for code readability
* @return
*/
public boolean isCreative() {
return gameMode == GameMode.CREATIVE;
}
public void setDimension(Dimension dimension) {
if (dimension == null)
throw new IllegalArgumentException("Dimension cannot be null!");

View File

@ -0,0 +1,19 @@
package net.minestom.server.entity.damage;
import net.minestom.server.entity.Player;
/**
* Represents a type of damage
*/
public class DamageType {
// TODO
public static final DamageType VOID = new DamageType();
public static final DamageType PLAYER = new DamageType();
public static DamageType fromPlayer(Player player) {
// TODO
return PLAYER;
}
}

View File

@ -140,17 +140,29 @@ public class Chunk implements Viewable {
}
public short getBlockId(int x, int y, int z) {
short id = blocksId[getBlockIndex(x, y, z)];
int index = getBlockIndex(x, y, z);
if(index < 0 || index >= blocksId.length) {
return 0; // TODO: custom invalid block
}
short id = blocksId[index];
return id;
}
public short getCustomBlockId(int x, int y, int z) {
short id = customBlocksId[getBlockIndex(x, y, z)];
int index = getBlockIndex(x, y, z);
if(index < 0 || index >= blocksId.length) {
return 0; // TODO: custom invalid block
}
short id = customBlocksId[index];
return id;
}
public CustomBlock getCustomBlock(int x, int y, int z) {
short id = customBlocksId[getBlockIndex(x, y, z)];
int index = getBlockIndex(x, y, z);
if(index < 0 || index >= blocksId.length) {
return null; // TODO: custom invalid block
}
short id = customBlocksId[index];
return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null;
}
@ -161,6 +173,9 @@ public class Chunk implements Viewable {
protected void refreshBlockValue(int x, int y, int z, short blockId, short customId) {
int blockIndex = getBlockIndex(x, y, z);
if(blockIndex < 0 || blockIndex >= blocksId.length) {
return;
}
this.blocksId[blockIndex] = blockId;
this.customBlocksId[blockIndex] = customId;

View File

@ -88,6 +88,14 @@ public abstract class Instance implements BlockModifier, DataContainer {
public abstract boolean hasEnabledAutoChunkLoad();
/**
* Determines whether a position in the void. If true, entities should take damage and die.
* Always returning false allow entities to survive in the void
* @param position the position in the world
* @return true iif position is inside the void
*/
public abstract boolean isInVoid(Position position);
//
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
ByteBuf chunkData = chunk.getFullDataPacket();

View File

@ -16,6 +16,7 @@ import net.minestom.server.particle.Particle;
import net.minestom.server.particle.ParticleCreator;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.ChunkUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.SerializerUtils;
import java.io.File;
@ -330,6 +331,12 @@ public class InstanceContainer extends Instance {
return autoChunkLoad;
}
@Override
public boolean isInVoid(Position position) {
// TODO: customizable
return position.getY() < -64;
}
protected void addSharedInstance(SharedInstance sharedInstance) {
this.sharedInstances.add(sharedInstance);
}

View File

@ -5,6 +5,7 @@ import net.minestom.server.entity.Player;
import net.minestom.server.instance.batch.BlockBatch;
import net.minestom.server.instance.batch.ChunkBatch;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import java.io.File;
import java.util.Collection;
@ -133,6 +134,11 @@ public class SharedInstance extends Instance {
return instanceContainer.hasEnabledAutoChunkLoad();
}
@Override
public boolean isInVoid(Position position) {
return instanceContainer.isInVoid(position);
}
@Override
public void setBlock(int x, int y, int z, short blockId, Data data) {
instanceContainer.setBlock(x, y, z, blockId, data);