Parauga ECS Spraudnis
In this guide you will learn how to create a simple poison system utilizing all of the features you previously learned about Hytale's ECS system
Rakstīja oskarscot, jacobwojoski
Praktisks Piemērs: Indes Sistēma
Saliekot visu iepriekš apgūto kopā, šādi var pilnveidot indes sistēmu. Kad tā ir piešķirta vienībai, tā atņem dzīvības ar noteiktu intervālu līdz termiņa beigām un sevi noņem.
package scot.oskar.hytaletemplate.components;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nullable;
public class PoisonComponent implements Component<EntityStore> {
private float damagePerTick;
private float tickInterval;
private int remainingTicks;
private float elapsedTime;
// Add static getter and setter to store componentType in the component instead of getting it from the plugin singleton.
// - This allows anyone to call PoisonComponent.getComponentType() instead of ExamplePlugin.get().getPoisonComponentType();
// - You could also update the setter to be a singleton if desired.
// - Adding a throw condition if getComponentType() returns null could also be good.
private static ComponentType<EntityStore, PoisonComponent> type;
public static ComponentType<EntityStore, PoisonComponent> getComponentType(){
return this.type;
}
public static void setComponentType(ComponentType<EntityStore, PoisonComponent> type){
this.type = type;
}
// A builder codec is needed to allow components to be saved and loaded from disk
public static final BuilderCodec<PoisonComponent> CODEC = BuilderCodec
.builder(PoisonComponent.class, PoisonComponent::new)
.append(
new KeyedCodec<>("DamagePerTick", Codec.FLOAT),
(component, value) -> component.damagePerTick = value,
component -> component.damagePerTick
).add()
.append(
new KeyedCodec<>("TickInterval", Codec.FLOAT),
(component, value) -> component.tickInterval = value,
component -> component.tickInterval
).add()
.append(
new KeyedCodec<>("RemainingTicks", Codec.INTEGER),
(component, value) -> component.remainingTicks = value,
component -> component.remainingTicks
)
.add()
.append(
new KeyedCodec<>("ElapsedTime", Codec.FLOAT),
(component, value) -> component.elapsedTime = value,
component -> component.elapsedTime
)
.add()
.build();
public PoisonComponent() {
this(5f, 1.0f, 10);
}
public PoisonComponent(float damagePerTick, float tickInterval, int totalTicks) {
this.damagePerTick = damagePerTick;
this.tickInterval = tickInterval;
this.remainingTicks = totalTicks;
this.elapsedTime = 0f;
}
public PoisonComponent(PoisonComponent other) {
this.damagePerTick = other.damagePerTick;
this.tickInterval = other.tickInterval;
this.remainingTicks = other.remainingTicks;
this.elapsedTime = other.elapsedTime;
}
@Nullable
@Override
public Component<EntityStore> clone() {
return new PoisonComponent(this);
}
public float getDamagePerTick() {
return damagePerTick;
}
public float getTickInterval() {
return tickInterval;
}
public int getRemainingTicks() {
return remainingTicks;
}
public float getElapsedTime() {
return elapsedTime;
}
public void addElapsedTime(float dt) {
this.elapsedTime += dt;
}
public void resetElapsedTime() {
this.elapsedTime = 0f;
}
public void decrementRemainingTicks() {
this.remainingTicks--;
}
public boolean isExpired() {
return this.remainingTicks <= 0;
}
}package scot.oskar.hytaletemplate.systems;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import scot.oskar.hytaletemplate.components.PoisonComponent;
public class PoisonSystem extends EntityTickingSystem<EntityStore> {
private final ComponentType<EntityStore, PoisonComponent> poisonComponentType;
public PoisonSystem(ComponentType<EntityStore, PoisonComponent> poisonComponentType) {
this.poisonComponentType = poisonComponentType;
}
@Override
public void tick(float dt, int index, @Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
@Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
// You could also just call PoisonComponent.getComponentType() instead of taking in the passed in variable here.
PoisonComponent poison = archetypeChunk.getComponent(index, poisonComponentType);
Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
poison.addElapsedTime(dt);
if (poison.getElapsedTime() >= poison.getTickInterval()) {
poison.resetElapsedTime();
Damage damage = new Damage(Damage.NULL_SOURCE, DamageCause.OUT_OF_WORLD, poison.getDamagePerTick());
DamageSystems.executeDamage(ref, commandBuffer, damage);
poison.decrementRemainingTicks();
}
if (poison.isExpired()) {
commandBuffer.removeComponent(ref, poisonComponentType);
}
}
@Nullable
@Override
public SystemGroup<EntityStore> getGroup() {
return DamageModule.get().getGatherDamageGroup();
}
@Nonnull
@Override
public Query<EntityStore> getQuery() {
return Query.and(this.poisonComponentType);
}
}package scot.oskar.hytaletemplate.commands;
public class ExampleCommand extends AbstractPlayerCommand {
public ExampleCommand() {
super("test", "Super test command!");
}
@Override
protected void execute(@Nonnull CommandContext commandContext, @Nonnull Store<EntityStore> store,
@Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef playerRef, @Nonnull World world) {
Player player = store.getComponent(ref, Player.getComponentType());
PoisonComponent poison = new PoisonComponent(3f, 0.5f, 8);
store.addComponent(ref, PoisonComponent.getComponentType(), poison);
player.sendMessage(Message.raw("You have been poisoned!").color(Color.GREEN).bold(true));
}
}package scot.oskar.hytaletemplate;
public final class ExamplePlugin extends JavaPlugin {
private static ExamplePlugin instance;
public ExamplePlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
}
@Override
protected void setup() {
this.getCommandRegistry().registerCommand(new ExampleCommand());
this.getEventRegistry().registerGlobal(PlayerReadyEvent.class, ExampleEvent::onPlayerReady);
this.getEventRegistry().registerGlobal(PlayerChatEvent.class, ChatFormatter::onPlayerChat);
// Register the component & set the type in the component class so its easier to retrieve.
ComponentType<EntityStore, PoisonComponent> poisonComponentType = this.getEntityStoreRegistry()
.registerComponent(PoisonComponent.class, PoisonComponent::new);
PoisonComponent.setComponentType(poisonComponentType);
this.getEntityStoreRegistry().registerSystem(new PoisonSystem(PoisonComponent.getComponentType()));
}
public static ExamplePlugin get() {
return instance;
}
}Vaicājums izmanto tikai poisonComponentType, kas nozīmē, ka sistēma apstrādās jebkuru entītiju ar PoisonComponent, ne tikai spēlētājus. Tas padara to elastīgu NPC, pūļu vai jebkuras dzīvas būtnes saindēšanai. Sistēma ievieto sevi GatherDamageGroup, tāpēc bojājumi plūst caur pilnu bojājumu caurvadu, ieskaitot bruņu efektivitātes samazināšanu un neievainojamības pārbaudes.