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

Commit 513a058

Browse files
committed
fix: a mirror sends the beam back, and the second plate gets asked
- HeldBeam resolves a tick as a bounded loop of segments - a deflected beam continues along the mirrored direction - a melted film hands the rest of the beam on in the same tick - the beam path reaches the client, so a bend is drawn as a bend - a round that got out keeps its travel and meets what stands beyond - REACH_EXHAUSTED separates running out of path from leaving
1 parent 4f8664a commit 513a058

14 files changed

Lines changed: 801 additions & 145 deletions

File tree

‎src/main/java/zmaster587/advancedRocketry/api/damage/StopReason.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ public enum StopReason {
1616
/** The path left the structure with budget to spare. Pairs with {@link DamageOutcome#EXITED}. */
1717
EXITED_FAR_SIDE,
1818

19+
/**
20+
* The granted PATH ran out while the body was still inside structure, with budget in hand.
21+
* Pairs with {@link DamageOutcome#EXITED}, because the budget is handed back either way — and
22+
* that shared outcome is exactly why this reason has to exist separately.
23+
*
24+
* <p>"Budget left over" and "came out the other side" are different facts, and a caller that
25+
* has to decide whether the body is still IN there can only tell them apart here. Reported as
26+
* {@code EXITED_FAR_SIDE} until 2026-08-20, which told a round that had bored a fifth of a
27+
* block that it had left the plate.</p>
28+
*/
29+
REACH_EXHAUSTED,
30+
1931
/**
2032
* The target region is not loaded, so nothing could be resolved. <b>Not</b> a statement that there
2133
* is nothing there — a caller able to retry should, and one that treats this as "clean miss"

‎src/main/java/zmaster587/advancedRocketry/client/ClientBeamTracker.java‎

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import net.minecraft.util.math.Vec3d;
44

5+
import java.util.ArrayList;
6+
import java.util.Collections;
7+
import java.util.List;
8+
59
import java.util.Collection;
610
import java.util.Map;
711
import java.util.concurrent.ConcurrentHashMap;
@@ -37,14 +41,17 @@ public final class ClientBeamTracker {
3741
private ClientBeamTracker() {
3842
}
3943

40-
/** This gun's beam is burning along this segment, as of now. */
41-
public static void lit(long gun, Vec3d from, Vec3d to) {
44+
/** This gun's beam is burning along this PATH, as of now. */
45+
public static void lit(long gun, List<Vec3d> path) {
46+
if (path == null || path.size() < 2) {
47+
return;
48+
}
4249
ClientBeam beam = BEAMS.get(gun);
4350
if (beam == null) {
44-
BEAMS.put(gun, new ClientBeam(from, to));
51+
BEAMS.put(gun, new ClientBeam(path));
4552
return;
4653
}
47-
beam.refresh(from, to);
54+
beam.refresh(path);
4855
}
4956

5057
/** This gun's beam has gone out. */
@@ -62,6 +69,24 @@ public static int count() {
6269
return BEAMS.size();
6370
}
6471

72+
/**
73+
* How many of them have a CORNER in them — a beam something turned.
74+
*
75+
* <p>The second observable, and it exists because the first cannot see the thing that goes
76+
* wrong here. A bent beam sent to a client as two ends is still one drawn beam, so a count of
77+
* beams is green whether the corner arrived or not; what a player would see is a laser drawn
78+
* straight through the mirror that turned it.</p>
79+
*/
80+
public static int bentCount() {
81+
int bent = 0;
82+
for (ClientBeam beam : BEAMS.values()) {
83+
if (beam.isBent()) {
84+
bent++;
85+
}
86+
}
87+
return bent;
88+
}
89+
6590
public static void clear() {
6691
BEAMS.clear();
6792
}
@@ -82,34 +107,47 @@ public static void tick() {
82107
BEAMS.values().removeIf(ClientBeam::ageAndCheckStale);
83108
}
84109

85-
/** One drawn beam: where it starts, where it ends, both in world coordinates. */
110+
/**
111+
* One drawn beam: the path it occupies, in world coordinates, muzzle first.
112+
*
113+
* <p>Two points for the ordinary beam, more where something turned it. {@link #getFrom} and
114+
* {@link #getTo} are kept because the ends are what most readers want, and because a beam that
115+
* has not been bent is exactly its two ends.</p>
116+
*/
86117
public static final class ClientBeam {
87118

88-
private Vec3d from;
89-
private Vec3d to;
119+
private List<Vec3d> path;
90120
private int sinceHeard;
91121

92-
private ClientBeam(Vec3d from, Vec3d to) {
93-
this.from = from;
94-
this.to = to;
122+
private ClientBeam(List<Vec3d> path) {
123+
this.path = new ArrayList<Vec3d>(path);
95124
}
96125

97-
private void refresh(Vec3d newFrom, Vec3d newTo) {
98-
from = newFrom;
99-
to = newTo;
126+
private void refresh(List<Vec3d> newPath) {
127+
path = new ArrayList<Vec3d>(newPath);
100128
sinceHeard = 0;
101129
}
102130

103131
private boolean ageAndCheckStale() {
104132
return ++sinceHeard > STALE_TICKS;
105133
}
106134

135+
/** Every point of the line, muzzle first. Never fewer than two. */
136+
public List<Vec3d> getPath() {
137+
return Collections.unmodifiableList(path);
138+
}
139+
140+
/** Whether something turned this beam, which is the only case the path has a corner. */
141+
public boolean isBent() {
142+
return path.size() > 2;
143+
}
144+
107145
public Vec3d getFrom() {
108-
return from;
146+
return path.get(0);
109147
}
110148

111149
public Vec3d getTo() {
112-
return to;
150+
return path.get(path.size() - 1);
113151
}
114152
}
115153
}

‎src/main/java/zmaster587/advancedRocketry/client/render/RenderBeams.java‎

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,25 +93,34 @@ public void onRenderWorldLast(RenderWorldLastEvent event) {
9393
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
9494

9595
for (ClientBeamTracker.ClientBeam beam : ClientBeamTracker.burning()) {
96-
Vec3d from = beam.getFrom();
97-
Vec3d to = beam.getTo();
98-
if (from == null || to == null) {
99-
continue;
96+
// A beam is a PATH: one leg for the ordinary one, more where a mirror turned it. Each
97+
// leg is a ribbon of its own because each faces the camera differently, and only the
98+
// LAST one ends in a spot — the corners are places the beam went on from, not places it
99+
// landed, and a glow at a corner would read as a hit that never happened.
100+
java.util.List<Vec3d> path = beam.getPath();
101+
for (int leg = 0; leg + 1 < path.size(); leg++) {
102+
Vec3d from = path.get(leg);
103+
Vec3d to = path.get(leg + 1);
104+
if (from == null || to == null) {
105+
continue;
106+
}
107+
Vec3d axis = to.subtract(from);
108+
if (axis.lengthVector() < 1.0E-6D) {
109+
continue;
110+
}
111+
axis = axis.normalize();
112+
Vec3d across = across(axis, from, to, eye);
113+
if (across == null) {
114+
continue;
115+
}
116+
ribbon(buffer, from, to, across.scale(HALO_HALF_WIDTH), eye,
117+
1.0F, 0.32F, 0.16F, 0.35F);
118+
ribbon(buffer, from, to, across.scale(CORE_HALF_WIDTH), eye,
119+
1.0F, 0.93F, 0.85F, 1.0F);
120+
if (leg + 2 == path.size()) {
121+
spot(buffer, to, axis, eye);
122+
}
100123
}
101-
Vec3d axis = to.subtract(from);
102-
if (axis.lengthVector() < 1.0E-6D) {
103-
continue;
104-
}
105-
axis = axis.normalize();
106-
Vec3d across = across(axis, from, to, eye);
107-
if (across == null) {
108-
continue;
109-
}
110-
ribbon(buffer, from, to, across.scale(HALO_HALF_WIDTH), eye,
111-
1.0F, 0.32F, 0.16F, 0.35F);
112-
ribbon(buffer, from, to, across.scale(CORE_HALF_WIDTH), eye,
113-
1.0F, 0.93F, 0.85F, 1.0F);
114-
spot(buffer, to, axis, eye);
115124
}
116125

117126
tessellator.draw();

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,13 @@ private WalkResult finish() {
342342
return result;
343343
}
344344
// Budget still in hand at the path limit: hand it back rather than absorb it silently.
345+
// WHERE it ran out is the caller's question, though, and the two answers are not the
346+
// same fact: still in the material with the path spent, or out the far side with path
347+
// to spare. A body that "exited" without leaving would be advanced past whatever stood
348+
// beyond it, which is not a thing it ever reached.
345349
result.outcome = DamageOutcome.EXITED;
346-
result.stopReason = StopReason.EXITED_FAR_SIDE;
350+
result.stopReason = previousWasSolid
351+
? StopReason.REACH_EXHAUSTED : StopReason.EXITED_FAR_SIDE;
347352
result.exitPoint = previousWasSolid ? farEnd : lastSolidExit;
348353
return result;
349354
}

‎src/main/java/zmaster587/advancedRocketry/network/PacketBeamState.java‎

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import net.minecraft.network.PacketBuffer;
77
import net.minecraft.util.math.BlockPos;
88
import net.minecraft.util.math.Vec3d;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
912
import zmaster587.advancedRocketry.client.ClientBeamTracker;
1013
import zmaster587.libVulpes.network.BasePacket;
1114

@@ -28,25 +31,35 @@
2831
*/
2932
public class PacketBeamState extends BasePacket {
3033

34+
/**
35+
* A beam is a PATH and not a segment, because something can turn it.
36+
*
37+
* <p>Two points for the ordinary beam. More where a mirror sent it back: the corner is a real
38+
* point on the line the beam occupies, and a bent beam drawn muzzle-to-end would be drawn
39+
* straight through the very plating that turned it. The cap is a wire bound and matches the
40+
* server's own segment budget, so a path that reaches it is drawn as far as it was resolved.</p>
41+
*/
42+
private static final int MAX_POINTS = 9;
43+
3144
private long gun;
3245
private boolean lit;
33-
private double fromX, fromY, fromZ;
34-
private double toX, toY, toZ;
46+
private final List<Vec3d> path = new ArrayList<Vec3d>(2);
3547

3648
public PacketBeamState() {
3749
}
3850

39-
public static PacketBeamState of(BlockPos gun, Vec3d from, Vec3d to, boolean lit) {
51+
public static PacketBeamState of(BlockPos gun, List<Vec3d> path, boolean lit) {
4052
PacketBeamState packet = new PacketBeamState();
4153
packet.gun = gun.toLong();
42-
packet.lit = lit && from != null && to != null;
54+
packet.lit = lit && path != null && path.size() >= 2;
4355
if (packet.lit) {
44-
packet.fromX = from.x;
45-
packet.fromY = from.y;
46-
packet.fromZ = from.z;
47-
packet.toX = to.x;
48-
packet.toY = to.y;
49-
packet.toZ = to.z;
56+
for (Vec3d point : path) {
57+
if (point == null || packet.path.size() >= MAX_POINTS) {
58+
break;
59+
}
60+
packet.path.add(point);
61+
}
62+
packet.lit = packet.path.size() >= 2;
5063
}
5164
return packet;
5265
}
@@ -59,12 +72,12 @@ public void write(ByteBuf out) {
5972
if (!lit) {
6073
return;
6174
}
62-
buffer.writeDouble(fromX);
63-
buffer.writeDouble(fromY);
64-
buffer.writeDouble(fromZ);
65-
buffer.writeDouble(toX);
66-
buffer.writeDouble(toY);
67-
buffer.writeDouble(toZ);
75+
buffer.writeByte(path.size());
76+
for (Vec3d point : path) {
77+
buffer.writeDouble(point.x);
78+
buffer.writeDouble(point.y);
79+
buffer.writeDouble(point.z);
80+
}
6881
}
6982

7083
@Override
@@ -75,12 +88,12 @@ public void readClient(ByteBuf in) {
7588
if (!lit) {
7689
return;
7790
}
78-
fromX = buffer.readDouble();
79-
fromY = buffer.readDouble();
80-
fromZ = buffer.readDouble();
81-
toX = buffer.readDouble();
82-
toY = buffer.readDouble();
83-
toZ = buffer.readDouble();
91+
int count = Math.min(MAX_POINTS, buffer.readUnsignedByte());
92+
path.clear();
93+
for (int i = 0; i < count; i++) {
94+
path.add(new Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()));
95+
}
96+
lit = path.size() >= 2;
8497
}
8598

8699
@Override
@@ -91,7 +104,7 @@ public void read(ByteBuf in) {
91104
@Override
92105
public void executeClient(EntityPlayer player) {
93106
if (lit) {
94-
ClientBeamTracker.lit(gun, new Vec3d(fromX, fromY, fromZ), new Vec3d(toX, toY, toZ));
107+
ClientBeamTracker.lit(gun, path);
95108
} else {
96109
ClientBeamTracker.extinguished(gun);
97110
}

0 commit comments

Comments
 (0)