Compare commits

...
1 Commits
Author SHA1 Message Date
dmg 0536d4c717 feat(presentation): hide cooling controls
Release / release (push) Successful in 8m43s
2026-09-21 00:26:10 -04:00
8 changed files with 79 additions and 2 deletions

No files matched your search

@@ -18,6 +18,7 @@ import org.bukkit.persistence.PersistentDataType;
* The caller must enforce session custody and suppress vanilla item uses; this class registers no events.
*/
public final class SessionControls {
public static final NamespacedKey COOLDOWN_MODEL = new NamespacedKey("fantasystick", "cooldown_control");
private final games.dmg.fantasystick.ability.AbilityRegistry registry;
private final Clock clock;
private static final NamespacedKey VERSION = key("control_version");
@@ -92,6 +93,7 @@ public final class SessionControls {
var meta = item.getItemMeta();
meta.displayName(Component.text(icon.name()));
meta.setMaxStackSize(1);
if (cooling) meta.setItemModel(COOLDOWN_MODEL);
var data = meta.getPersistentDataContainer();
data.set(VERSION, PersistentDataType.INTEGER, 1);
data.set(OWNER, PersistentDataType.STRING, owner.toString());
@@ -172,6 +172,8 @@ class AbilityEngineNativeTest {
assertTrue(cooled.cooldowns().get("test_focus").isAfter(java.time.Instant.now()));
var cooling = a.core.inventory.getItem(2);
assertEquals(Material.GRAY_STAINED_GLASS_PANE, cooling.getType());
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
cooling.getItemMeta().getItemModel());
assertEquals(net.kyori.adventure.text.Component.text("Test Focus — Cooling Down"), cooling.getItemMeta().displayName());
assertEquals("test_focus", new games.dmg.fantasystick.items.SessionControls(registry).ability(cooling).orElseThrow());
@@ -189,6 +191,8 @@ class AbilityEngineNativeTest {
a.core.inventory.setItemInOffHand(sheet); f.activate(a);
a.core.inventory.getInventory().setSelectedSlot(2);
assertEquals(Material.GRAY_STAINED_GLASS_PANE, a.core.inventory.getItem(2).getType());
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
a.core.inventory.getItem(2).getItemMeta().getItemModel());
assertEquals("test_focus", new games.dmg.fantasystick.items.SessionControls(registry)
.ability(a.core.inventory.getItem(2)).orElseThrow());
f.dispatcher.accept(new org.bukkit.event.player.PlayerInteractEvent(a.player, Action.RIGHT_CLICK_AIR,
@@ -1078,13 +1078,22 @@ class EquipmentSessionsNativeTest {
assertTrue(f.sessions.acknowledgeProgress(active, learned.revision(), cooled));
var pane = actor.inventory.getItem(2).clone();
assertEquals(Material.GRAY_STAINED_GLASS_PANE, pane.getType());
assertEquals(SessionControls.COOLDOWN_MODEL, pane.getItemMeta().getItemModel(),
"Only the acknowledged persisted cooldown control selects the cosmetic model");
assertFalse(f.sessions.acknowledgeProgress(active, current.revision(), learned),
"A stale pre-cooldown acknowledgment cannot replace the current presentation");
assertEquals(SessionControls.COOLDOWN_MODEL,
actor.inventory.getItem(2).getItemMeta().getItemModel());
f.sessions.exit(actor.player); f.state(actor); f.activate(actor);
assertEquals(Material.GRAY_STAINED_GLASS_PANE, actor.inventory.getItem(2).getType(),
"Resume reconstructs the future persisted deadline");
assertEquals(SessionControls.COOLDOWN_MODEL, actor.inventory.getItem(2).getItemMeta().getItemModel());
clock.advance(Duration.ofSeconds(30));
f.sessions.refreshCooldownControls(1);
assertEquals(Material.SNOWBALL, actor.inventory.getItem(2).getType());
assertNull(actor.inventory.getItem(2).getItemMeta().getItemModel(),
"Exact-deadline restoration replaces the whole control without stale cosmetic metadata");
assertNotEquals(pane, actor.inventory.getItem(2), "The retired pane is no longer installed");
assertEquals(deadline, f.sessions.currentRecord(f.sessions.active(actor.id).orElseThrow()).orElseThrow()
.cooldowns().get("fireball"), "Visual restoration does not rewrite the deadline");
@@ -88,11 +88,15 @@ class FireballNativeTest {
assertTrue(a.messages.stream().anyMatch(message -> message.contains("ready in")));
var deadline = storage.find(a.core.record.id()).get().orElseThrow().cooldowns().get("fireball");
assertTrue(deadline.isAfter(java.time.Instant.now()));
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
a.core.inventory.getItem(2).getItemMeta().getItemModel());
var sheet = a.core.inventory.getItemInOffHand().clone();
a.core.inventory.setItemInOffHand(null); f.changed(a); PartyBookNativeTest.drain(f); f.awaitCharacterAndReceipt(a);
a.core.inventory.setItemInOffHand(sheet); f.activate(a);
a.core.inventory.getInventory().setSelectedSlot(2);
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
a.core.inventory.getItem(2).getItemMeta().getItemModel());
assertEquals("fireball", new games.dmg.fantasystick.items.SessionControls(
(games.dmg.fantasystick.ability.AbilityRegistry) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "abilityRegistry"))
.ability(a.core.inventory.getItem(2)).orElseThrow());
@@ -145,6 +149,8 @@ class FireballNativeTest {
assertTrue(new games.dmg.fantasystick.items.SessionControls(
(games.dmg.fantasystick.ability.AbilityRegistry) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "abilityRegistry"))
.ability(a.core.inventory.getItem(2)).isEmpty());
assertNull(a.core.inventory.getItem(2).getItemMeta().getItemModel(),
"Unlearning replaces the cooling control and leaves no stale invisible model");
PartyBookNativeTest.click(f, a, PartyBookNativeTest.button(a, "[+1 WIS]"));
PartyBookNativeTest.drain(f); PartyBookNativeTest.drain(f);
@@ -154,6 +160,9 @@ class FireballNativeTest {
PartyBookNativeTest.click(f, a, PartyBookNativeTest.button(a, "[Learn]"));
PartyBookNativeTest.drain(f); PartyBookNativeTest.drain(f);
assertEquals(deadline, storage.find(a.core.record.id()).get().orElseThrow().cooldowns().get("fireball"));
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
a.core.inventory.getItem(2).getItemMeta().getItemModel(),
"Relearning reconstructs the retained future cooldown model");
cast(f, a); assertEquals(1, launches.get());
assertTrue(a.messages.stream().anyMatch(message -> message.contains("ready in")));
@@ -164,6 +173,8 @@ class FireballNativeTest {
b.core.inventory.getInventory().setSelectedSlot(2);
assertEquals(org.bukkit.Material.GRAY_STAINED_GLASS_PANE, b.core.inventory.getItem(2).getType(),
"The transferred character reconstructs its retained cooldown pane");
assertEquals(games.dmg.fantasystick.items.SessionControls.COOLDOWN_MODEL,
b.core.inventory.getItem(2).getItemMeta().getItemModel());
assertEquals("fireball", new games.dmg.fantasystick.items.SessionControls(
(games.dmg.fantasystick.ability.AbilityRegistry) FantasyStickLifecycleNativeTest.Fixture.field(f.plugin, "abilityRegistry"))
.ability(b.core.inventory.getItem(2)).orElseThrow());
@@ -48,6 +48,7 @@ class SessionControlsNativeTest {
item.getItemMeta().displayName());
assertEquals(1, item.getAmount());
assertEquals(1, item.getMaxStackSize());
assertNull(item.getItemMeta().getItemModel(), "Book, basic attack and No Ability remain unaffected");
var expected = new SessionControls.Identity(owner, session, slot);
assertEquals(expected, controls.identify(item).orElseThrow());
assertTrue(controls.owned(item, owner, session));
@@ -82,6 +83,7 @@ class SessionControlsNativeTest {
var item = roundTrip(items[abilitySlot + 2]);
assertEquals(materials.get(index), item.getType(), ids.get(index));
assertEquals(Component.text(names.get(index)), item.getItemMeta().displayName());
assertNull(item.getItemMeta().getItemModel(), "Ready ability icons remain unaffected");
assertEquals(ids.get(index), item.getItemMeta().getPersistentDataContainer()
.get(key("control_ability_id"), PersistentDataType.STRING));
assertEquals(1, item.getAmount());
@@ -113,9 +115,24 @@ class SessionControlsNativeTest {
assertEquals("cooldown", pane.getPersistentDataContainer().get(key("control_kind"), PersistentDataType.STRING));
assertEquals(java.util.Optional.of("fireball"), timed.ability(pane));
assertEquals(new SessionControls.Identity(owner, session, slot), timed.identify(pane).orElseThrow());
assertEquals(SessionControls.COOLDOWN_MODEL, pane.getItemMeta().getItemModel());
}
assertEquals(Material.ECHO_SHARD, items[4].getType(), "A deadline equal to now is ready");
assertNull(items[4].getItemMeta().getItemModel(), "An exact-deadline ready icon retains no stale model");
assertEquals(Material.PAPER, items[5].getType(), "Unknown saved IDs remain unavailable, not cooling panes");
assertNull(items[5].getItemMeta().getItemModel(), "Unknown controls remain visible and unaffected");
var genuineWithoutModel = items[2].clone();
var genuineMeta = genuineWithoutModel.getItemMeta();
genuineMeta.setItemModel(null);
genuineWithoutModel.setItemMeta(genuineMeta);
assertEquals(java.util.Optional.of("fireball"), timed.ability(genuineWithoutModel),
"Cosmetic model metadata is not casting or session authority");
var ordinaryPane = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
var ordinaryMeta = ordinaryPane.getItemMeta();
ordinaryMeta.setItemModel(SessionControls.COOLDOWN_MODEL);
ordinaryPane.setItemMeta(ordinaryMeta);
assertRejected(ordinaryPane);
}
@Test
@@ -0,0 +1,21 @@
{
"model": {
"type": "minecraft:select",
"property": "minecraft:display_context",
"cases": [
{
"when": [
"firstperson_lefthand",
"firstperson_righthand"
],
"model": {
"type": "minecraft:empty"
}
}
],
"fallback": {
"type": "minecraft:model",
"model": "minecraft:item/gray_stained_glass_pane"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"pack": {
"description": "Fantasy Stick ${version} sheet presentation",
"description": "Fantasy Stick ${version} first-person presentation",
"min_format": [88, 0],
"max_format": [88, 0]
}
@@ -106,7 +106,7 @@ class DistributionTest {
}
}
@Test void packagesVersionedContextSensitiveSheetResourcePackAndMatchingRuntimeMetadata() throws Exception {
@Test void packagesVersionedContextSensitiveResourcePackAndMatchingRuntimeMetadata() throws Exception {
Path artifact = Path.of(System.getProperty("distribution.jar"));
Path pack = Path.of(System.getProperty("resource.pack.zip"));
String version = System.getProperty("distribution.version");
@@ -129,6 +129,19 @@ class DistributionTest {
assertTrue(json.contains("firstperson_righthand"));
assertTrue(json.contains("minecraft:empty"));
assertTrue(json.contains("minecraft:item/paper"), "Inventory and other contexts retain Paper presentation");
var cooldownModel = zip.getEntry("assets/fantasystick/items/cooldown_control.json");
assertNotNull(cooldownModel, "The single pack includes the dedicated cooldown-control model");
String cooldownJson;
try (var input = zip.getInputStream(cooldownModel)) {
cooldownJson = new String(input.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
}
assertTrue(cooldownJson.contains("minecraft:select"));
assertTrue(cooldownJson.contains("minecraft:display_context"));
assertTrue(cooldownJson.contains("firstperson_lefthand"));
assertTrue(cooldownJson.contains("firstperson_righthand"));
assertTrue(cooldownJson.contains("minecraft:empty"));
assertTrue(cooldownJson.contains("minecraft:item/gray_stained_glass_pane"),
"Hotbar, inventory and all other contexts retain gray-pane presentation");
}
try (var jar = new JarFile(artifact.toFile())) {
var metadata = jar.getJarEntry("sheet-resource-pack.properties"); assertNotNull(metadata);