package com.example.tests;
import com.frotty27.hrtk.api.annotation.HytaleSuite;
import com.frotty27.hrtk.api.annotation.WorldTest;
import com.frotty27.hrtk.api.annotation.Tag;
import com.frotty27.hrtk.api.annotation.DisplayName;
import com.frotty27.hrtk.api.annotation.Order;
import com.frotty27.hrtk.api.assert_.HytaleAssert;
import com.frotty27.hrtk.api.assert_.PhysicsAssert;
import com.frotty27.hrtk.api.context.WorldTestContext;
import com.frotty27.hrtk.api.lifecycle.IsolationStrategy;
@HytaleSuite(value = "Physics Surface Tests", isolation = IsolationStrategy.DEDICATED_WORLD)
@Tag("physics")
public class PhysicsSurfaceTests {
@WorldTest
@Order(1)
@DisplayName("Spawned entity on solid ground is stationary")
void spawnedEntityIsStationary(WorldTestContext ctx) {
// Build a solid floor first, then spawn on top of it.
ctx.fillRegion(-5, 63, -5, 5, 63, 5, "Rock_Sandstone");
Object entity = ctx.spawnNPC("Kweebec_Sapling", 0, 64, 0);
ctx.waitTicks(5);
Object store = ctx.getStore();
// assertOnGround checks the grounded flag on the physics component.
PhysicsAssert.assertOnGround(store, entity);
// assertStationary checks that the entity's speed is below the tolerance.
// A small tolerance (0.01) accounts for floating-point imprecision.
PhysicsAssert.assertStationary(store, entity, 0.01);
}
@WorldTest
@Order(2)
@DisplayName("Read velocity components from a moving entity")
void readVelocityFromEntity(WorldTestContext ctx) {
ctx.fillRegion(-5, 63, -5, 5, 63, 5, "Rock_Sandstone");
Object entity = ctx.spawnNPC("Trork_Warrior", 0, 64, 0);
ctx.waitTicks(2);
Object store = ctx.getStore();
// getVelocity returns a double array [vx, vy, vz].
double[] velocity = ctx.getVelocity(store, entity);
HytaleAssert.assertNotNull("Velocity should not be null", velocity);
HytaleAssert.assertEquals(3, velocity.length);
}
@WorldTest
@Order(3)
@DisplayName("Entity in air is detected as not on ground")
void entityInAirDetected(WorldTestContext ctx) {
// Spawn the entity high above the ground with no floor beneath.
Object entity = ctx.spawnNPC("Kweebec_Sapling", 0, 200, 0);
ctx.waitTicks(1);
Object store = ctx.getStore();
// The entity is falling - it should be detected as in the air.
PhysicsAssert.assertInAir(store, entity);
}
@WorldTest
@Order(4)
@DisplayName("Apply a force and verify the entity gains speed")
void applyForceGivesSpeed(WorldTestContext ctx) {
ctx.fillRegion(-5, 63, -5, 5, 63, 5, "Rock_Sandstone");
Object entity = ctx.spawnNPC("Trork_Warrior", 0, 64, 0);
ctx.waitTicks(2);
Object store = ctx.getStore();
// Apply a horizontal force impulse in the +X direction.
ctx.addForce(store, entity, 5.0, 0.0, 0.0);
ctx.waitTicks(1);
// After the force is applied, the entity's speed should be above zero.
double speed = ctx.getSpeed(store, entity);
HytaleAssert.assertTrue(
"Speed should be greater than zero after force application",
speed > 0.0
);
}
@WorldTest
@Order(5)
@DisplayName("Set velocity directly and verify it was applied")
void setVelocityDirectly(WorldTestContext ctx) {
ctx.fillRegion(-5, 63, -5, 5, 63, 5, "Rock_Sandstone");
Object entity = ctx.spawnNPC("Kweebec_Sapling", 0, 64, 0);
ctx.waitTicks(2);
Object store = ctx.getStore();
// setVelocity overrides the current velocity with exact values.
ctx.setVelocity(store, entity, 1.0, 2.0, 3.0);
ctx.waitTicks(1);
// Verify the velocity was set. Note: gravity and friction will have
// modified the values slightly by the time we read them one tick later.
PhysicsAssert.assertVelocity(store, entity, 1.0, 2.0, 3.0, 1.0);
}
@WorldTest
@Order(6)
@DisplayName("Verify speed assertion with tolerance")
void speedAssertionWithTolerance(WorldTestContext ctx) {
ctx.fillRegion(-5, 63, -5, 5, 63, 5, "Rock_Sandstone");
Object entity = ctx.spawnNPC("Outlander_Hunter", 0, 64, 0);
ctx.waitTicks(5);
Object store = ctx.getStore();
// A stationary entity should have speed near 0.
PhysicsAssert.assertSpeed(store, entity, 0.0, 0.1);
}
}