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

Combat: guns you build, shields that spend energy to hold, and hulls that break where they are hit - #27

Draft
StannisMod wants to merge 36 commits into
1.12from
feature/war-shields-turrets
Draft

Combat: guns you build, shields that spend energy to hold, and hulls that break where they are hit#27
StannisMod wants to merge 36 commits into
1.12from
feature/war-shields-turrets

Conversation

@StannisMod

@StannisMod StannisMod commented Aug 20, 2026

Copy link
Copy Markdown
Owner

A ship or a base can now be shot at, and can shoot back. Weapons are built out of blocks and are
worth what was built. A shot exists between muzzle and impact, crossing space nobody is watching. A
shield spends stored energy to keep that shot out, and whatever gets through breaks the hull where it
lands, permanently. The rest of this describes what that costs, and what it does not do.

What it does to a server

No weapon here damages a living thing, which is not the same as being safe for a crew. Rounds and
beams spend themselves on blocks: a turret will track a player or a mob, hold a firing solution and
shoot, and the target takes no damage from what arrives. What kills people is the hull. A breach vents
the atmosphere behind it through the oxygen system exactly as any other hole does, so opening a sealed
station to vacuum is a way to kill everyone in it, and the fact that no round ever touched them does
not help them. Crew-versus-crew fire is not implemented; killing a crew is.

A world carries at most Weapons.maxShotsPerWorld rounds at once, 256 by default. Fire beyond
that is refused rather than queued, and nothing already in the air is dropped to make room, so the
cost of a battery firing continuously is bounded by that number rather than by how many turrets
someone built. A round that reaches an unloaded position stops there instead of damaging what it
cannot see.

Weapon fire asks before it takes a block, and a refusal is honoured. Every destruction posts a
break event that a claim mod, a region plugin or an admin's own listener can cancel; vanilla spawn
protection is consulted directly, since it is not a listener. A refused block keeps the damage it took
and stays standing, absorbing the energy that would have destroyed it, and the round carries on with
what is left of its budget rather than stopping at the boundary. The break is attributed to a fixed
synthetic player, name [weapon-fire], id b6ab6a37-2b1a-4b0a-9d9f-6e2f2f5f0a11 — stable across
restarts, so a protection system can be told about it by either.

A destroyed block does not come back. The hand welder below walks a damaged block back one stage at
a time, but a hole stays a hole: there is no rebuild, no rollback and no admin sweep for it in this
change. A raid leaves a permanent hole in the map, and that is the state a server has to be willing to
live with.

Two config keys switch the whole thing off, and nothing else drives it. With
Weapons.enableProjectileSubstrate = false no shot is admitted, nothing is stepped, and anything
still in the air is ended rather than suspended, so nothing is left in the save to resume later. Since
a fired round is the only thing that ever declares an impact, that key also ends structural damage.
With Weapons.enableFireControlSensor = false the sensor block still exists, acquires nothing,
publishes nothing and draws no power, so batteries are aimed by hand.

One existing config key changed meaning, and packs already set it. Rocket.partsWearSystem used
to decide both where wear accumulates and whether a worn part had any consequence; it now governs
accumulation only. A pack that switched it off to stop career wear will find that a rocket seat staged
by a shell still refuses a crewed launch. Automated launches are unaffected.

Most of the new content is craftable, and the defensive half is not. Recipes ship for the turret
mount, the barrel, the ammunition feed, the cooling jacket, the weapons console, the fire-control
sensor and the repair welder. The beam emitter, the mirror and reactive plating families, and the
shield blocks have none yet. On a survival server that means the means of attack are reachable and the
means of defence are not, so anyone shipping this before the recipe pass should expect to supply that
half.

Weapons

A turret is an assembly, and every number it fires with comes from the blocks around it. The
controller walks its own build and derives muzzle speed, impact energy, fire interval, spread, traverse
rate, heat capacity and buffer size from what it finds.

A build with beam emitters holds a continuous beam; a build without them throws rounds. The thrower
spends energy per shot at an interval. The beam spends energy every tick it is lit, reaches 64 blocks,
and cuts deeper the longer it stays on one spot. A build is one or the other, never half of each, and both
leave by the same muzzle and meet armour under the same rules.

A starved weapon goes dark and saves up rather than stuttering. Below the price of one tick the beam
goes out, banks a second's worth of burn with no supply at all, then lights again, which turns a weak supply into
bursts a player can time. A gun reports holding fire and having nothing left as separate
states, which is the difference between a battery that is waiting and one that is beaten.

A gun needs no network to work, and needs permission to be commanded. Target, charge and a barrel
are enough: on the ground, aboard a ship, wired to nothing. A weapons console points a whole battery at
one contact and can feed it faster. Who may issue that command is decided by the gun's owner, faction
tag and access code, all three set on the gun itself.

Fire control

A battery can find its own target instead of waiting to be pointed. The sensor sweeps for contacts,
screens them for friend or foe, ranks what survives and hands the battery one.

Holding a target well enough to shoot at is a separate question from seeing it. Every contact
carries a lock quality, and Weapons.fireControlSensorLockQualityToFire is the floor below which a gun
will not fire on it. A sensor that listens draws no power; one that illuminates draws it every tick and
buys a better lock, making the mode a running cost weighed against how badly the shot is needed.

Projectiles

A fired round is a server-side record, not an entity. It needs no loaded chunk to exist. Each tick
it advances along the volume it swept, and it ends for one stated reason at a world point that can be
shown to a player. Rounds in flight survive a save and resume where they were.

Penetration takes time. A round is granted only the depth left in the current tick, pays what that
depth costs, and continues from there on the next one. A thin wall is a through-and-through, a thick
one stops the round inside it, and holding fire on one spot digs.

A solid round skips off metal at a glancing angle. Incidence decides it and only metal deflects. A deflection costs
speed, which is what ends a round trapped between two plates.

A round inside a moving hull moves with it. A ship that manoeuvres while a round is boring through
its armour carries that round along, instead of leaving it at the coordinates the ship has left.

Fire is drawn for the people it passes. The visibility test is the round's path rather than its
muzzle: a player within Weapons.shotVisibilityRadius of that path is told about it, which is what
makes a shot fired from four kilometres away visible to the person it is aimed at. A held beam is drawn
for as long as it burns. Setting the radius to zero silences the drawing channel while the mechanics
carry on.

Shields

A shield prices what it stops by what kind of thing arrived. A strike declares its kind and its
energy; the shell pays out of what it stores and regenerates. A fully paid strike carrying a body can
be thrown back off the shell. A beam has no body to return, so it cannot be, and that is why lasers are
the answer to a shield where slugs are the answer to armour.

A damaged shield covers less ground and costs the same to run. Shrinking coverage is the one
consequence of shield damage visible before the shell fails, and the energy bill does not shrink with
it.

Structural damage

A hull remembers where it was hit. An impact declares a point, a direction, an energy and a kind;
the engine works out which blocks it reaches, how deep it gets and what breaks. Blocks pass through
stages before they are destroyed, and the stages are held per position, so the record outlives the
block.

Damage travels with the structure it belongs to. Capture a battered build into a rocket or a
station assembly, paste it elsewhere, relocate a ship: the stages and the record of what was destroyed
go along.

A damaged machine behaves differently rather than behaving normally until it dies. A turret that has
taken hits traverses slowly, then seizes, and keeps firing along whatever bearing it seized on. A
damaged shield emitter projects less. Each unit is told what broke it, its own destruction included, so
a subsystem decides its own consequence instead of having one applied from outside.

Repair starts with a hand welder. It walks a staged block back one stage at a time, paying out of
its own buffer, and charging more for a block further gone. Holes it cannot help with.

Armour

Armour answers for itself. A block a shot reaches can stop it, pass it on with what is left, send
it off on a new course, or decline and take whatever the ordinary rules do. It answers on any face, and
a block that implements none of this behaves as it always did.

Mirror plating returns a beam and is glass to everything else. It reflects the fraction its grade
reflects and sheds the remainder as heat. Hold more power on it than it can shed and the film burns
out, after which the rest of the beam continues into whatever stood behind it. A solid round is not
reflected at all: the plating declines, and breaks like the thin pane it is. Aluminium, silver and gold
grades are available.

Reactive plating swallows a bounded amount of arriving energy once. Splinters and light fire are
eaten whole. A heavy round goes through as though the plating were absent, since what it carries dwarfs
one charge. A spent charge removes itself, which is why two layers stop more than one: the second is
asked only after the first is gone.

What a block costs to shoot through is pack-tunable. config/advRocketry/weights.json gained
toughness and ablation columns beside the weight ones, resolved in the same order: explicit name, then
pattern, then material, then fallback. Existing files load unchanged, with absent sections falling back
to shipped defaults. The file is rewritten on save, so hand-maintained comments and key order in it are
not preserved.

Internal surface

Nothing outside this repository is known to build against these.

  • New public packages api.damage, api.projectile, api.weapon and api.sensor, carrying the
    impact, shot, gun-spec and contact types the subsystems exchange.
  • New shared package subsystem.network: connected components plus a max-flow solve, with a status
    vocabulary naming which constraint bound a domain. The shield network was moved onto it, and AFFS's
    own IShieldSource, IShieldSink, IShieldCable, IShieldNetworkNode and ShieldNetworkRegistry
    were deleted in favour of a thin domain wrapper. ShieldStrike and ShieldStrikeResult now carry a
    kind and an optional body.
  • Three server-to-client packets added at the end of the packet registry (shot spawn, shot end, beam
    state); existing discriminator positions are unchanged.
  • One per-world saved data holding the shots in flight and the world's impact-identity counter.
  • Two files in the in-tree Valkyrien Skies copy now notify the damage record when a ship's blocks are
    relocated, so a deconstructed ship is not handed back repaired.

- add optional body velocity to ShieldStrike
- add reflected outcome carrying the new velocity
- mirror using the entity path's own law
- add shieldStrikeReflectionRestitution tunable, default 1.0
- key kinetic semantics on a body, not Entity
- extend artest shield strike with a body triple
- pin reflection, pricing and short-pay penetration
- add the impact request/report pair as public API
- add ImpactKind with one declared shield mapping
- spend a budget along a voxel ray into blocks
- add a toughness column to the weight tables
- store stages for blocks with no tile
- record what a destroyed block was
- resolve a ship target along the impact ray
- convert world to subspace inside the service
- refuse a repeated impact identity
- add artest damage probes
- keep the damage map per-world; no block owns it
- carry records through capture/paste, VS assembly and deconstruction
- sweep destroyed positions as a region: holes have no block to ride
- select over the cut box, measure from the block bounds
- forget a record when a player breaks or replaces that block
- add a damage records probe, a relocation e2e and layer unit tests
- one use removes one stage, priced from the block's own recipe
- Forge Energy on the tool; any charger in the pack fills it
- five distinct outcomes; a refusal takes neither material nor charge
- add an artest damage weld probe and a server e2e
- add the shot record, its per-world registry and the tick integrator
- test every step as a swept segment, never a point in solid
- order the field and structure layers by earliest crossing, geometrically
- probe the shield before committing, through one shell search
- end a shot below the reflection speed floor instead of parking it
- share one definition of "there is structure here" with the damage engine
- add /artest shot and a read-only chunk-loaded probe
- carry the end point beside the end reason, world-frame always
- pin a shot stopping at the hull of a ship that has moved
- pin a shot bouncing off a charged shell and staying up
- report endX/endY/endZ from the shot probe
- traverse the impact path voxel-exactly instead of sampling it
- move SweptSegment to util so damage need not depend on the substrate
- pin that a bore has no untouched block inside it, at any angle
- keep the axis-aligned case as the control that nothing else moved
- add the gun contract: parts derive a built gun's numbers
- assemble by connectivity, re-walked when blocks change
- express traverse as a mechanism with a failure vocabulary
- convert muzzle point and aim separately, inherit ship motion
- do nothing aboard a ship VS has not named yet
- hold fire when the shooter's own structure blocks the muzzle
- replicate shots by path proximity, draw tracers and impacts
- draw the barrel, replicate the command rather than the pose
- cherry-pick the shared subsystem network from life_support
- add the turret probe verb and a client tile-nbt read
- add the weapons console as a stateless network editor
- assign a target by linker, hold fire without losing it
- clear the battery's target when its last console goes
- add the weaponconsole probe verb and its e2e
- follow an entity, not only a point, and lose it when it dies
- spare a target carrying the installation's access code
- declare what a build needs delivered, gas included and reserved
- add manual control as a mode through the same launch path
- read mount telemetry off the guns for the console readout
- pin the ship-frame case: a gun in the shipyard fires into the world
…old it

- a fire-control sensor: sweep, classify, hand the network one contact
- range from total radiated power, lock quality from radiance alone
- passive listens and emits nothing; active illuminates and pays for it
- an ally never enters the contact list, nor does anyone aboard our ship
- guns lead an acquired target and hold fire on a lock too poor to hit
- a mount reads its own block's condition; nothing notifies it
- damaged, then seized: a ladder of named states, not a scalar
- a seized mount holds its bearing and keeps firing down it
- a damaged part gives less of what it gives, negatives included
- the wear flag gates accrual only, so battle damage is never free
- AFFS solved its own max flow with the same status vocabulary; that copy is gone
- tiles implement the subsystem node interfaces directly, the bridge layer with them
- a console keeps its own setting when its network disappears
- our strike path is untouched: reflection, restitution and the impact tests all stand

Cherry-picked from feature/life_support:
  2a492f3 refactor: one network for every subsystem, not one per subsystem
  3d2ad59 fix: a console no longer wipes its own setting when its network is gone
  bd6262d refactor: collapse the AFFS shield-network bridge layer
Ventilation-only files and probes were left behind; those blocks do not exist here.
- emitter radius splits into declared (billed) and projected (geometry)
- projected radius derived from the block's own damage stage, replicated
- generator, cable and accumulator each deliver less when damaged
- two shield config coefficients, either at zero disables its consequence
- probe reports both radii, the cycle cost and per-point coverage
- Contact, ContactResult and IContactResponder in the public damage API
- the substrate obeys the answer; the default law is the old behaviour
- SweptSegment reports the face each voxel was entered through
- contact geometry rides the block's own frame, not the world's
- a hull crossing grants only the path travelled this tick
- spending energy costs speed, for bodies whose speed it is coupled to
- stage price scales with the body's cross-section, reference area unchanged
- a resumed bore is not charged twice for the block it stands in
- strike kind comes from the hull-kind to shield-kind mapping
- a body velocity is declared only for kinds that carry mass
- a beam is absorbed by a charged shell, never mirrored
- e2e falsified against the previous behaviour
- a shot inside material is kept in that hull's subspace between ticks
- ShotFrame is the one seam: whole conversion or none of it
- velocity carries the hull's own motion in and back out again
- the first crossing of an embedded tick asks that hull alone
- NBT hull key states which frame the coordinates are in
- SweptVolume: exact along the axis, quadrature across it, layers
- a body under half a block wide is the ray exactly, so nothing shipped moves
- a layer's budget is divided by overlap share, never multiplied
- each block pays for the area it is under, not for the whole body
- the crossing test sweeps the body, so a wide round can graze
- shotBodyRadiusCap bounds the geometry, never the price
- it rests inside, and its crater deepens while it is still in there
- two plates prove a round's own continuation is not deduped away
- the contact seam takes a body's facts, not the shot record
- a stage is priced by the kind that arrived: pushed through, or boiled away
- a block with no ablation row has one derived from its toughness
- a glancing solid round skips off metal, and off nothing else
- the probe fill clears recorded damage, so a rebuilt wall is fresh
…rough

- the threshold sits above the affordability line of metal
- refused on price alone, a faint beam went clean through the plate
- the probe reports both columns, so neither is read off a depth
- each armour scenario gets its own lane: a round outlives its own wall
- mirror and reactive plating cling to the face they were applied to
- a mirror reflects a fraction and dies by what its film absorbs
- a reactive charge eats a portion and spends only itself
- a block is priced by how much of its voxel it fills, not by owning one
- occupancy comes from the collision list: stairs are boxes, not a cube
- a round goes further through panes than through solid glass
- mint impact ids from ShotRegistry, one monotonic counter per world
- drop the shot id/sequence packing that collided after 256 impacts
- key the dedup memory by dimension: ids are minted per world
- move probe-declared identities out of production's id space
- add ShotCrossingTrace and the probes that tell refusal from miss
- remove the workaround the armour e2e carried for this
- 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
- ContactResult.noOpinion: the default law decides, as for a plain block
- IContactResponder no longer tells implementers to decline by passing through
- mirror plating declines a solid round instead of waving it through free
- one lowercase regex row prices the mirror family as glass, not hull plate
…body leaves a gun

- LayerCrossing: field-or-structure ordering lifted out of the shot's own step
- TurretFireControl.muzzleOf: the standoff, the frame and the line-of-fire refusal
- both were correct only while exactly one caller asked them
- DamageOccurrence: cause, kind, severity, stages, place, hull - facts only
- IDamageAware via capability: told, never asked, no storage - news is not state
- the engine records what it advanced; the service publishes cause and hull
- a dying unit's listener is captured before its block becomes air
- ImpactRequest.withCause, so the cause enum is not decoration
- harness attaches a recorder, the route a foreign mod would take
- HeldBeam: one tick of a line with a power, no record and nothing to persist
- the shield is struck every tick, priced by kind and carrying no body to mirror
- GunSpec.beamPowerPerTick and a gunBeamEmitter part, so a beam can be built
- a starved beam goes dark and saves a quantum instead of stuttering
- isOperable means CAN DELIVER: a beam has no barrel and is a weapon anyway
- the buffer is sized from the quantum, or a beam can never light at all
- replicate a held beam as a repeated state with a staleness backstop
- one place decides who can see a line, for rounds and beams alike
- the gun owns its channel; every not-burning path announces itself
- draw the beam as billboarded ribbons with a spot where it lands
- pin the cadence contract and the arrival on a real client
- take both sides' lang keys; the two blocks touch nothing in common
@StannisMod StannisMod added feature New gameplay capability system A subsystem or its model, not a single mechanic labels Aug 20, 2026
- the literal named ordinary world coordinates after the region moved
- the gun there assembled correctly and the test called that a regression
- post a break event a protection mod can cancel, and honour the refusal
- consult vanilla spawn protection directly; it is no listener
- end the rounds still in the air when the substrate is switched off
- a stand-in claim listener behind the test command, so the veto path is real
@StannisMod
StannisMod marked this pull request as ready for review August 20, 2026 13:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a4ab55191

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +27 to +32
public final class WeaponNetworkDomain extends SubsystemNetworkDomain {

public static final WeaponNetworkDomain INSTANCE = new WeaponNetworkDomain();

private WeaponNetworkDomain() {
super("Weapon");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add weapon-domain source and cable nodes

A repo-wide search of every ISubsystemSource and ISubsystemCable implementation finds only the AFFS generator, accumulator, and cable, all of which bind to the shield domain. Consequently a WeaponNetworkDomain component can never contain a source or cable: SubsystemNetworkManager always publishes it as source-empty/disconnected, no shared FE is delivered, and consoles can command only weapon nodes that are directly face-adjacent rather than the advertised remote battery.

Useful? React with 👍 / 👎.

Comment on lines +102 to +104
if (result.isIntercepted()) {
Vec3d at = result.getHitPoint() == null ? contact : result.getHitPoint();
return new Emission(at, first.distance, true, false, 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Let partial shield interceptions pass their residual beam

When a shield has some energy but cannot afford the whole beam tick, ShieldStrikeService returns isIntercepted() == true with positive residual impact energy. This branch treats every interception as a complete stop and returns residual zero, so the shield absorbs the otherwise-penetrating remainder for free on the tick that drains it; recurse beyond the crossing with getResidualImpactEnergy() unless the result is fully absorbed.

Useful? React with 👍 / 👎.

Comment on lines +117 to +120
ContactResolver.Resolution resolved = ContactResolver.resolve(world, body, first.structure,
reach - first.distance, false);
int residual = resolved.result.isStopped() ? 0 : resolved.result.getResidualEnergy();
return new Emission(contact, first.distance, false, true, residual);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Continue held beams along contact deflections

When a held beam meets mirror plating, ContactResolver returns a deflected ContactResult containing the reflected velocity, but this path reads only whether it stopped and its residual energy. The emission therefore ends at the mirror and no reflected energy travels or damages anything along the returned direction, so the new mirror armour does not actually reflect held beam weapons even though it correctly reflects registry-backed projectiles.

Useful? React with 👍 / 👎.

Comment on lines +374 to +378
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
// Nothing of its own to save: the network owns the target and the hold-fire switch, and a
// console that persisted a copy would come back disagreeing with them.
return super.writeToNBT(nbt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist the weapon network's control state

After a world unload or server restart, SubsystemNetworkManager discards its in-memory WorldState, while this console persists none of the WeaponNetworkState fields. The rebuilt network therefore resets its target, hold-fire flag, and access code; in particular, a battery left on hold can resume sensor-driven firing and entities carrying the formerly configured friendly code are no longer filtered. Persist the network-owned settings in world data or through an authoritative controller rather than dropping every copy.

Useful? React with 👍 / 👎.

Comment on lines +781 to +786
AdvancedRocketryBlocks.blockGunBeamEmitter = new zmaster587.advancedRocketry.block.weapon.BlockGunPart(
builder -> builder.addBeamPowerPerTick(4_000).setKind(
zmaster587.advancedRocketry.api.damage.ImpactKind.BEAM)
.addHeatPerShot(1).addHeatCapacity(20)
.declareInput(zmaster587.advancedRocketry.api.weapon.GunInput.FORGE_ENERGY))
.setUnlocalizedName("gunBeamEmitter").setCreativeTab(tabAdvRocketry);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ship the beam emitter's resources

The beam emitter is the only part that makes a constructed gun a beam, but a repo-wide resource search finds no gunBeamEmitter blockstate/model, localization entry, or crafting recipe, unlike every other new gun part. It is therefore unobtainable in survival and appears unlocalized with a missing model when obtained through creative mode or commands, leaving the held-beam feature inaccessible through normal gameplay.

Useful? React with 👍 / 👎.

Comment on lines +110 to +112
if (shipId == null) {
remember(world, request.getImpactId());
StructureDamageEngine.WalkResult walked = StructureDamageEngine.penetrate(world, point,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Delay deduplication until an unloaded target is ruled out

If the first voxel of a ground or ship-frame damage walk is unloaded, StructureDamageEngine returns TARGET_UNLOADED with the full budget so the caller can retry, but the impact ID has already been remembered here. Retrying the same deferred request is then reported as a duplicate for 600 ticks and the impact can never apply after the chunk loads; only remember the ID once the walk has actually applied an outcome.

Useful? React with 👍 / 👎.

Comment on lines +40 to +42
public void setTarget(Vec3d target) {
this.target = target;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear entity targets when assigning a point

When a console assigns a point after previously assigning a live entity, this setter leaves targetEntity populated. TileTurret.getEffectiveTarget() checks the tracked entity before the point, so the successful point assignment is silently ignored until that entity dies or unloads; setting a point should clear the entity order, just as clearTarget() clears both fields.

Useful? React with 👍 / 👎.

@StannisMod
StannisMod marked this pull request as draft August 20, 2026 14:16
…ody kept

- one enableWeapons gate for firing, acquisition and weapon damage
- a beam asks the gate too, and a refused shot costs no heat
- weapon fire asks protection before removing armour plating
- six model assets renamed to the case a jar looks them up in
- the beam emitter gets a blockstate, a recipe and a name
- weights.json writes back every column it reads
- console and sensor readouts go through the lang catalogue
- partsWearSystem describes what it actually gates
- 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
Development continues in the standalone StannisMod/Stellurgy repository as of 2026-08-31. This repository keeps the Advanced Rocketry line: history, issue and pull request numbering, and the fork relationships.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

feature New gameplay capability system A subsystem or its model, not a single mechanic

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant