Skip to content
This repository was archived by the owner on Aug 31, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
638bc7d
feat: reflect a declared travelling body off the shield
StannisMod Aug 14, 2026
61dc624
feat: damage structure with a declared impact
StannisMod Aug 14, 2026
a5e1750
feat: damage travels with the structure, not with a block
StannisMod Aug 15, 2026
026d76a
feat: a hand welder, the bottom rung of the repair ladder
StannisMod Aug 15, 2026
4dfe1bc
feat: a shot is a record that crosses distance nobody is watching
StannisMod Aug 15, 2026
55f874b
feat: a shot that ends says where, in coordinates a player can see
StannisMod Aug 15, 2026
db6ef31
fix: bore the path a round crossed, not the points sampled along it
StannisMod Aug 15, 2026
82f4df0
feat: a gun that needs no network, and a round you can see
StannisMod Aug 17, 2026
965d8fc
feat: one console points a whole battery, and owns nothing
StannisMod Aug 17, 2026
f59cce9
feat: a gun that tracks a target and knows whose side it is on
StannisMod Aug 17, 2026
f682785
feat: a battery that finds its own target, and knows when it cannot h…
StannisMod Aug 17, 2026
d21cba1
feat: a shot-up gun turns slowly, then seizes, and still shoots
StannisMod Aug 17, 2026
e09f0f7
refactor: shields ride the shared subsystem network, not their own copy
StannisMod Aug 17, 2026
2789c18
feat: a shot-up shield covers less, and still pays full price
StannisMod Aug 17, 2026
a065d3f
feat: the block a shot meets answers what happens to it
StannisMod Aug 17, 2026
81d8be5
feat: a round bores over ticks instead of ending at the surface
StannisMod Aug 18, 2026
ace00c2
fix: a shell is told what kind of shot it stopped
StannisMod Aug 18, 2026
cf378e2
feat: a round drilling a hull goes where the ship goes
StannisMod Aug 18, 2026
f8f7551
feat: a shot meets the world with its body, not its centre line
StannisMod Aug 18, 2026
2f79c7e
test: what a round does inside a hull it cannot get through
StannisMod Aug 19, 2026
368684f
feat: a hull answers by kind and by angle
StannisMod Aug 19, 2026
0dad292
feat: a beam below the intensity threshold is absorbed, not passed th…
StannisMod Aug 19, 2026
9e2c8fa
feat: armour that answers, on any face, priced by the volume it fills
StannisMod Aug 19, 2026
a39b9c8
fix: an impact identity the world hands out, not one a shot assembles
StannisMod Aug 19, 2026
d20df0a
fix: a block pays for the volume it has, with no floor invented under it
StannisMod Aug 19, 2026
1b4af48
feat: a responder may decline, and a mirror is glass again
StannisMod Aug 19, 2026
6b91ac2
refactor: ask one place which layer a segment meets, and one where a …
StannisMod Aug 20, 2026
a67d7bb
feat: a unit hears what broke it, its own destruction included
StannisMod Aug 20, 2026
b7c8925
feat: a beam that is held, not thrown
StannisMod Aug 20, 2026
4a74c0b
feat: a beam that is seen while it is held
StannisMod Aug 20, 2026
f3212e9
Merge 1.12 into the war branch
StannisMod Aug 20, 2026
cbab4d8
test: ask the allocator where the shipyard is, do not write it down
StannisMod Aug 20, 2026
8a4ab55
fix: a weapon asks before it takes a block, and off means ended
StannisMod Aug 20, 2026
4f8664a
fix: the war switch, the assets nobody could see, and the columns nob…
StannisMod Aug 21, 2026
513a058
fix: a mirror sends the beam back, and the second plate gets asked
StannisMod Aug 21, 2026
f0ef5f9
docs: point this repository at Stellurgy
StannisMod Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
> ## ➡️ Development has moved to [StannisMod/Stellurgy](https://github.com/StannisMod/Stellurgy)
>
> **This repository is no longer where Stellurgy is developed.** As of **2026-08-31** the project
> continues in its own standalone repository: **https://github.com/StannisMod/Stellurgy**
>
> This one keeps the **Advanced Rocketry line**: the full history up to the split, every issue and
> pull request number ever opened here, and the fork relationship to
> [Advanced-Rocketry/AdvancedRocketry](https://github.com/Advanced-Rocketry/AdvancedRocketry) and to
> the forks descending from it. Nothing was deleted and nothing was rewritten.
>
> **Why:** GitHub renders a fork's parent by its *current* name. While this repository was called
> "Stellurgy", every fork of it advertised that it had been forked from Stellurgy — which reverses
> the actual order of events, since those forks were taken from here when it was still Advanced
> Rocketry. Separating the two lets each name mean what it says.
>
> - **New issues, pull requests and development** → [StannisMod/Stellurgy](https://github.com/StannisMod/Stellurgy)
> - **Older references** (`#20`–`#27`, and anything from before the split) live here and are **not**
> redirected.
>
> The text below describes the project as of the split and is kept for context.

---

# Stellurgy

**Stellurgy is still in development. There is no public build yet.**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import com.github.stannismod.affs.AdvancedForceFieldSystem;
import com.github.stannismod.affs.item.ItemBlockTiered;
import com.github.stannismod.affs.te.TileEntityShieldCable;
import com.github.stannismod.affs.world.shield.IShieldNetworkNode;
import com.github.stannismod.affs.world.shield.ShieldNetworkManager;
import zmaster587.advancedRocketry.subsystem.network.ISubsystemNetworkNode;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
Expand Down Expand Up @@ -213,7 +214,10 @@ private AxisAlignedBB buildBoundingBox(IBlockState state) {

private boolean canConnect(IBlockAccess world, BlockPos pos, EnumFacing facing) {
TileEntity tileEntity = world.getTileEntity(pos.offset(facing));
return tileEntity instanceof IShieldNetworkNode;
// A cable connects to shield nodes only: a ventilation duct laid through the same wall is a
// network node too, and joining it would draw an arm to a block this line never feeds.
return tileEntity instanceof ISubsystemNetworkNode
&& ((ISubsystemNetworkNode) tileEntity).getNetworkDomain() == ShieldNetworkManager.DOMAIN;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,31 @@ public final class ModConfig {
// multi-emitter network squeezed through one thin line. Tunable, never balance-pinned.
public static int cableThroughputPerTick = 20_000;

// A shield node's condition drives what it DELIVERS. Two coefficients because the two consequences
// are different in kind, not in size: a scalar node (generator / cable / accumulator) simply moves
// less, while an emitter projects a SMALLER SPHERE — the one consequence a player can see coming,
// before the shell collapses. Each is the fraction lost at the last stage before destruction; 0
// disables that consequence entirely. Neither touches what the shield COSTS: a shrunken emitter is
// still billed for the radius it was told to hold, or being shot would save energy.
public static double shieldNodeDamagePenaltyMax = 0.75D;
public static double emitterRadiusDamagePenaltyMax = 0.5D;

// D134-2 tier-1 cooperative weapon interaction (axis-G tunable, never balance-pinned):
// - shieldStrikeAbsorptionRate: shield energy spent per unit of a cooperative strike's declared
// impact energy. spent = min(stored, impactEnergy x rate x kindMult / tierEff).
// - shieldStrikeDamageToEnergyFactor: converts a cooperating source's *damage* value to declared
// impact energy when it reports damage rather than energy.
// - shieldStrikeReflectionRestitution: how much of a declared travelling body's relative speed
// survives the mirror. 1.0 is a perfect mirror — exactly what the per-tick entity scan does — so
// at the default the two populations behave identically; the knob exists so they can be split
// later without touching the entity compatibility path. It scales SPEED ONLY: the absorption
// cost stays impactEnergy x rate x kindMult / tierEff, because one impact must have exactly one
// pricing path. Clamped to [0, 1] — above 1 the shell would hand out energy it never absorbed.
// The tier-2 residual hitscan-ray hook (a blanket World.rayTraceBlocks mixin) is deferred to its own
// task; its config lands with it, not here.
public static double shieldStrikeAbsorptionRate = 1.0D;
public static double shieldStrikeDamageToEnergyFactor = 500.0D;
public static double shieldStrikeReflectionRestitution = 1.0D;

private ModConfig() {
}
Expand Down Expand Up @@ -235,6 +251,28 @@ public static void sync() {
+ "your emitters, not by pipe-sizing. Lower it to make plumbing a real constraint."
);

shieldNodeDamagePenaltyMax = configuration.getFloat(
"shieldNodeDamagePenaltyMax",
CATEGORY_SHIELD,
0.75F,
0.0F,
1.0F,
"How much of a shield generator's conversion, a cable's transport or an accumulator's "
+ "reserve is lost when the block is one stage from destruction. 0 makes battle "
+ "damage free for these blocks."
);

emitterRadiusDamagePenaltyMax = configuration.getFloat(
"emitterRadiusDamagePenaltyMax",
CATEGORY_SHIELD,
0.5F,
0.0F,
1.0F,
"How much of an emitter's radius is lost when the block is one stage from destruction. "
+ "The field visibly draws in, uncovering whatever it used to reach; the emitter is "
+ "still billed for its declared radius, so damage never saves energy."
);

generatorShieldBuffer = configuration.getInt(
"generatorShieldBuffer",
CATEGORY_BUFFERS,
Expand Down Expand Up @@ -293,6 +331,18 @@ public static void sync() {
+ "source reports damage rather than energy."
);

shieldStrikeReflectionRestitution = configuration.getFloat(
"shieldStrikeReflectionRestitution",
CATEGORY_WEAPONS,
1.0F,
0.0F,
1.0F,
"Fraction of a declared travelling body's relative speed that survives reflection off the "
+ "shell. 1.0 is a perfect mirror (what the per-tick entity scan does). Scales speed "
+ "only — the absorption cost is unaffected. Applies to declared strikes that carry a "
+ "body; the entity scan is never affected."
);

if (configuration.hasChanged()) {
configuration.save();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ protected void actionPerformed(GuiButton button) throws IOException {
}

if (button.id == 0 || button.id == 1) {
int radius = tile.getRadius();
// The SETTING, not what a damaged emitter currently manages to project: stepping from the
// shrunken radius would quietly re-declare the field smaller every time it was nudged.
int radius = tile.getDeclaredRadius();
radius += button.id == 0 ? -1 : 1;
AdvancedForceFieldSystem.NETWORK.sendToServer(new PacketSetFieldRadius(tile.getPos(), radius));
}
Expand All @@ -57,7 +59,11 @@ protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, i
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
drawTitle(I18n.format("tile.field_generator.name"));
drawStat(I18n.format("gui.affs.access_code"), tile.getAccessCode(), rowY(0));
drawStat(I18n.format("gui.affs.radius"), tile.getRadius(), rowY(2));
// Projected / declared while damage is holding the field in, so the panel says which of the two
// numbers the bill below is charging for; one number while they agree.
drawStat(I18n.format("gui.affs.radius"), tile.getRadius() == tile.getDeclaredRadius()
? String.valueOf(tile.getDeclaredRadius())
: tile.getRadius() + " / " + tile.getDeclaredRadius(), rowY(2));
drawStat(I18n.format("gui.affs.shield_accumulator"), tile.getEnergyStored() + " / " + tile.getMaxEnergyStored(), rowY(5));
drawStat(I18n.format("gui.affs.tier"), (tile.getTier() + 1) + " / 4", rowY(6));
drawStat(I18n.format("gui.affs.impact_efficiency"), String.format(Locale.ROOT, "%.2fx", tile.getImpactEfficiencyMultiplier()), rowY(7));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import com.github.stannismod.affs.world.FieldSurfaceMath;
import com.github.stannismod.affs.world.contour.ContourFrameGeometry;
import com.github.stannismod.affs.world.projectile.IEnergyProjectile;
import com.github.stannismod.affs.world.shield.IShieldSink;
import com.github.stannismod.affs.world.shield.ShieldNetworkManager;
import com.github.stannismod.affs.world.shield.ShieldNetworkRegistry;
import com.github.stannismod.affs.world.shield.ShieldNetworkState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
Expand All @@ -25,8 +23,12 @@
import net.minecraft.world.Explosion;

import javax.annotation.Nullable;
import zmaster587.advancedRocketry.subsystem.network.ISubsystemSink;
import zmaster587.advancedRocketry.subsystem.network.SubsystemNetworkDomain;
import zmaster587.advancedRocketry.subsystem.network.SubsystemNetworkManager;
import zmaster587.advancedRocketry.subsystem.network.SubsystemNetworkRegistry;

public class TileEntityContourInjector extends TileEntity implements ITickable, IShieldSink {
public class TileEntityContourInjector extends TileEntity implements ITickable, ISubsystemSink {

public static final int MAX_SCAN_RADIUS = 16;
public static final int MAX_SHIELD_BUFFER = 200_000;
Expand Down Expand Up @@ -82,7 +84,7 @@ public void update() {

frameCount = geometry.getFrameCount();
interiorCount = geometry.getInteriorCount();
requestedShieldEnergy = getFreeShieldCapacity();
requestedShieldEnergy = getFreeCapacity();

refreshFieldActiveState(true);

Expand Down Expand Up @@ -119,29 +121,34 @@ public void update() {
public void onLoad() {
super.onLoad();
if (world != null && !world.isRemote) {
ShieldNetworkRegistry.register(this);
ShieldNetworkManager.markDirty(world);
SubsystemNetworkRegistry.register(this);
SubsystemNetworkManager.markDirty(ShieldNetworkManager.DOMAIN, world);
}
}

@Override
public void invalidate() {
if (world != null && !world.isRemote) {
ShieldNetworkRegistry.unregister(this);
ShieldNetworkManager.markDirty(world);
SubsystemNetworkRegistry.unregister(this);
SubsystemNetworkManager.markDirty(ShieldNetworkManager.DOMAIN, world);
}
super.invalidate();
}

@Override
public void onChunkUnload() {
if (world != null && !world.isRemote) {
ShieldNetworkRegistry.unregister(this);
ShieldNetworkManager.markDirty(world);
SubsystemNetworkRegistry.unregister(this);
SubsystemNetworkManager.markDirty(ShieldNetworkManager.DOMAIN, world);
}
super.onChunkUnload();
}

@Override
public SubsystemNetworkDomain getNetworkDomain() {
return ShieldNetworkManager.DOMAIN;
}

@Override
public BlockPos getNodePos() {
return pos;
Expand All @@ -153,21 +160,21 @@ public net.minecraft.world.World getNodeWorld() {
}

@Override
public int getRequestedShieldEnergy() {
return currentGeometry == null ? 0 : getFreeShieldCapacity();
public int getRequested() {
return currentGeometry == null ? 0 : getFreeCapacity();
}

@Override
public int getFreeShieldCapacity() {
public int getFreeCapacity() {
return Math.max(0, MAX_SHIELD_BUFFER - shieldBuffer);
}

@Override
public int receiveShieldEnergy(int amount) {
public int receive(int amount) {
if (world == null || world.isRemote || amount <= 0) {
return 0;
}
int accepted = Math.min(amount, getFreeShieldCapacity());
int accepted = Math.min(amount, getFreeCapacity());
if (accepted > 0) {
shieldBuffer += accepted;
shieldReceivedThisTick += accepted;
Expand Down
Loading