Skip to content
This repository was archived by the owner on Aug 31, 2026. It is now read-only.

Commit d20df0a

Browse files
committed
fix: a block pays for the volume it has, with no floor invented under it
- drop MIN_OCCUPANCY: a multiplier cannot express a detach cost - occupancyOf answers [0, 1] honestly; STAGE_COST_BASE holds the bottom - pin the contract: one material, two volumes, never free
1 parent a39b9c8 commit d20df0a

2 files changed

Lines changed: 52 additions & 11 deletions

File tree

src/main/java/zmaster587/advancedRocketry/damage/StructureDamageEngine.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,6 @@ public final class StructureDamageEngine {
5858
*/
5959
private static final int GAP_TOLERANCE = 6;
6060

61-
/**
62-
* The least of its voxel a block is ever treated as filling. A pane is not a wall and should not
63-
* cost like one, but nothing is free either: a body still has to break the thing off its mounting,
64-
* and a floor here is what stops a torch or a tripwire from being a hole in a hull.
65-
*/
66-
private static final double MIN_OCCUPANCY = 0.1D;
67-
6861
/** One whole voxel at the origin — the box a block is asked to report its collision shape within. */
6962
private static final AxisAlignedBB FULL_VOXEL = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
7063

@@ -419,7 +412,7 @@ public static int stageCost(World world, BlockPos pos, double areaFactor, Impact
419412
}
420413

421414
/**
422-
* How much of its voxel this block actually fills, in {@code [MIN_OCCUPANCY, 1]}.
415+
* How much of its voxel this block actually fills, in {@code [0, 1]}.
423416
*
424417
* <h3>Why a price has to know this at all</h3>
425418
* <p>The law is an energy per unit of VOLUME removed — that is the whole of why the mechanical and
@@ -437,6 +430,18 @@ public static int stageCost(World world, BlockPos pos, double areaFactor, Impact
437430
* states its real shape, box by box, so that is what is summed. Overlapping boxes would double
438431
* count, which is why the sum is clamped: over-counting can only ever produce "a full cube", the
439432
* answer we started from.</p>
433+
*
434+
* <h3>There is no floor under it, and nothing is free anyway</h3>
435+
* <p>A floor was tried and removed: it was a MULTIPLIER, so it priced "the least a block can cost"
436+
* out of that block's own material, and what it was meant to represent — the work of breaking a
437+
* thing off its mounting — has nothing to do with what the thing is made of. What actually keeps a
438+
* near-empty voxel from being free is {@code STAGE_COST_BASE}, which is material-independent and
439+
* already inside the product: a standing torch answers 0.024 here and still costs 6 against the
440+
* 1000 a full block of stone costs. Below that the price itself floors at 1.</p>
441+
*
442+
* <p>The floor's stated reason — that a torch must not be a hole in a hull — does not survive
443+
* being looked at: a voxel holding a torch is a voxel holding no hull block. The hole is the
444+
* builder's, and pricing it dearly does not fill it.</p>
440445
*/
441446
private static double occupancyOf(World world, BlockPos pos) {
442447
if (world == null || pos == null) {
@@ -451,17 +456,19 @@ private static double occupancyOf(World world, BlockPos pos) {
451456
volume += (box.maxX - box.minX) * (box.maxY - box.minY) * (box.maxZ - box.minZ);
452457
}
453458
if (volume > 0.0D) {
454-
return Math.max(MIN_OCCUPANCY, Math.min(1.0D, volume));
459+
return Math.min(1.0D, volume);
455460
}
456461
// No collision at all — a torch, a plant, a tripwire. It is still SOMETHING, so it falls
457462
// back to the shape it draws itself with rather than to nothing.
458463
AxisAlignedBB drawn = state.getBoundingBox(world, pos);
459464
if (drawn == null) {
460-
return MIN_OCCUPANCY;
465+
// It states no shape at all. The price floors at 1 rather than at nothing, which is
466+
// the whole of what "still SOMETHING" needs to mean here.
467+
return 0.0D;
461468
}
462469
double drawnVolume = (drawn.maxX - drawn.minX) * (drawn.maxY - drawn.minY)
463470
* (drawn.maxZ - drawn.minZ);
464-
return Math.max(MIN_OCCUPANCY, Math.min(1.0D, drawnVolume));
471+
return Math.min(1.0D, drawnVolume);
465472
} catch (RuntimeException blockDidNotLikeBeingAsked) {
466473
// A block may compute its shape from neighbours it expects to be loaded. It costs a full
467474
// cube rather than throwing, which is the answer that changes nothing.

src/test/java/zmaster587/advancedRocketry/test/server/StructuralDamageContractTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,40 @@ public void aTougherWallIsNotPenetratedFurtherThanAFlimsyOneAtEqualBudget() thro
141141
+ "its crew nothing.\niron=" + iron, ironDestroyed < glassDestroyed);
142142
}
143143

144+
/**
145+
* Two blocks of ONE material, differing only in how much of their voxel they fill: a wool block
146+
* and a wool carpet. The carpet must cost far less — and must still cost something.
147+
*
148+
* <p>The law is an energy per unit of VOLUME removed, so the material is deliberately held fixed:
149+
* a comparison across materials would pass on the toughness table alone and say nothing about
150+
* volume. A carpet fills a sixteenth of its voxel, and for as long as a block was priced as a full
151+
* cubic metre it cost what a solid block of wool costs to shoot through.</p>
152+
*
153+
* <p>The second half is the one that used to be held by a floor under the occupancy, and is now
154+
* held by the price itself: the base term of the law is material-independent and the price rounds
155+
* up to at least one. So "almost no material" lands at "almost free", never at "free" — which is
156+
* what stops a body walking an arbitrarily long run of decoration for nothing.</p>
157+
*/
158+
@Test
159+
public void aBlockIsPricedByHowMuchOfItsVoxelItFillsAndNeverAtNothing() throws Exception {
160+
int blockX = 1200, blockZ = 1300;
161+
int carpetX = 1200, carpetZ = 1320;
162+
buildWall("minecraft:wool", blockX, blockZ, 1);
163+
buildWall("minecraft:carpet", carpetX, carpetZ, 1);
164+
165+
String solid = stage(blockX, blockZ);
166+
String thin = stage(carpetX, carpetZ);
167+
long solidCost = readLong(solid, "stageCost");
168+
long thinCost = readLong(thin, "stageCost");
169+
170+
assertTrue("a carpet costs what a solid block of the same wool costs (carpet=" + thinCost
171+
+ " block=" + solidCost + "): then a block is priced as a full cubic metre of material"
172+
+ " however little of its voxel it fills, and the law stops being about volume."
173+
+ " carpet=" + thin + " block=" + solid, thinCost < solidCost);
174+
assertTrue("a carpet costs nothing at all (" + thin + "): then a body crosses any length of"
175+
+ " decoration for free, and the price has no lower end", thinCost >= 1);
176+
}
177+
144178
/** The unified stage reader at a wall's first block: stage, max stage, and what a stage costs there. */
145179
private String stage(int x, int z) throws Exception {
146180
return exec("artest damage stage " + DIM + " " + x + " " + Y + " " + z);

0 commit comments

Comments
 (0)