Compare commits

..

11 commits

Author SHA1 Message Date
Anuken
34be5ffc6c Defer shadow buffer init 2025-11-30 15:32:44 -05:00
Anuken
1dedefd56c Log an error and skip shadows instead of crashing when mods set maxSchematicSize too high 2025-11-30 14:49:16 -05:00
Anuken
3acbd1afed Merge remote-tracking branch 'origin/master' 2025-11-30 12:10:15 -05:00
Anuken
8b9fc34811 Enforce map image import sizes 2025-11-30 12:10:09 -05:00
EggleEgg
a0fe9e8415
Shootpattern allow replacing per bullet (ammo type support) (#11402) 2025-11-30 12:04:44 -05:00
EggleEgg
da6aecf695
change pneumatic drill's water usage from 3.6/s to 3.5/s (#11412) 2025-11-30 12:02:15 -05:00
Anuken
3791a7f375 Fixed #11403 2025-11-30 11:50:10 -05:00
Anuken
15efb721a0 Fixed #11399 2025-11-30 11:10:13 -05:00
Anuken
7130b6155c Fixed #11395 2025-11-30 11:04:47 -05:00
Anuken
44428c1f60 Fixed #11409 2025-11-30 11:03:20 -05:00
Anuken
f2232c15b1 Fixed #11404 2025-11-30 11:01:40 -05:00
10 changed files with 82 additions and 40 deletions

View file

@ -158,6 +158,7 @@ const UnlockEvent = Packages.mindustry.game.EventType.UnlockEvent
const StateChangeEvent = Packages.mindustry.game.EventType.StateChangeEvent
const CoreChangeEvent = Packages.mindustry.game.EventType.CoreChangeEvent
const BuildTeamChangeEvent = Packages.mindustry.game.EventType.BuildTeamChangeEvent
const TileOverlayChangeEvent = Packages.mindustry.game.EventType.TileOverlayChangeEvent
const TileFloorChangeEvent = Packages.mindustry.game.EventType.TileFloorChangeEvent
const TileChangeEvent = Packages.mindustry.game.EventType.TileChangeEvent
const TilePreChangeEvent = Packages.mindustry.game.EventType.TilePreChangeEvent
@ -185,6 +186,7 @@ const LaunchItemEvent = Packages.mindustry.game.EventType.LaunchItemEvent
const SectorInvasionEvent = Packages.mindustry.game.EventType.SectorInvasionEvent
const SectorLoseEvent = Packages.mindustry.game.EventType.SectorLoseEvent
const SaveLoadEvent = Packages.mindustry.game.EventType.SaveLoadEvent
const ContentPatchLoadEvent = Packages.mindustry.game.EventType.ContentPatchLoadEvent
const WorldLoadEndEvent = Packages.mindustry.game.EventType.WorldLoadEndEvent
const WorldLoadBeginEvent = Packages.mindustry.game.EventType.WorldLoadBeginEvent
const WorldLoadEvent = Packages.mindustry.game.EventType.WorldLoadEvent

View file

@ -2362,6 +2362,7 @@ public class Blocks{
bridgeConduit = new LiquidBridge("bridge-conduit"){{
requirements(Category.liquid, with(Items.graphite, 4, Items.metaglass, 8));
floating = true;
fadeIn = moveArrows = false;
arrowSpacing = 6f;
range = 4;
@ -2372,6 +2373,7 @@ public class Blocks{
phaseConduit = new LiquidBridge("phase-conduit"){{
requirements(Category.liquid, with(Items.phaseFabric, 5, Items.silicon, 7, Items.metaglass, 20, Items.titanium, 10));
floating = true;
range = 12;
arrowPeriod = 0.9f;
arrowTimeScl = 2.75f;
@ -2864,7 +2866,7 @@ public class Blocks{
drillTime = 400;
size = 2;
consumeLiquid(Liquids.water, 0.06f).boost();
consumeLiquid(Liquids.water, 3.5f / 60f).boost();
}};
laserDrill = new Drill("laser-drill"){{

View file

@ -108,6 +108,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
ui.loadAnd(() -> {
try{
Pixmap pixmap = new Pixmap(file);
//if you want to bypass the limit, use mods or the console; larger maps are not supported
if(pixmap.width > MapResizeDialog.maxSize || pixmap.height > MapResizeDialog.maxSize){
throw new Exception("Image is too large (maximum size is " + MapResizeDialog.maxSize + "x" + MapResizeDialog.maxSize + ")");
}
editor.beginEdit(pixmap);
pixmap.dispose();
}catch(Exception e){

View file

@ -24,21 +24,25 @@ public class Lightning{
private static boolean bhit = false;
private static int lastSeed = 0;
/** Create a lighting branch at a location. Use Team.derelict to damage everyone. */
public static void create(BulletType bulletCreated, Team team, Color color, float damage, float x, float y, float targetAngle, int length){
createLightningInternal(null, bulletCreated, lastSeed++, team, color, damage, x, y, targetAngle, length);
}
/** Create a lighting branch at a location. Use Team.derelict to damage everyone. */
public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){
createLightningInternal(null, lastSeed++, team, color, damage, x, y, targetAngle, length);
createLightningInternal(null, Bullets.damageLightning, lastSeed++, team, color, damage, x, y, targetAngle, length);
}
/** Create a lighting branch at a location. Uses bullet parameters. */
public static void create(Bullet bullet, Color color, float damage, float x, float y, float targetAngle, int length){
createLightningInternal(bullet, lastSeed++, bullet.team, color, damage, x, y, targetAngle, length);
createLightningInternal(bullet, bullet == null || bullet.type.lightningType == null ? Bullets.damageLightning : bullet.type.lightningType, lastSeed++, bullet.team, color, damage, x, y, targetAngle, length);
}
private static void createLightningInternal(@Nullable Bullet hitter, int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
private static void createLightningInternal(@Nullable Bullet hitter, BulletType hitCreate, int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
random.setSeed(seed);
hit.clear();
BulletType hitCreate = hitter == null || hitter.type.lightningType == null ? Bullets.damageLightning : hitter.type.lightningType;
Seq<Vec2> lines = new Seq<>();
bhit = false;

View file

@ -15,6 +15,7 @@ import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.part.*;
import mindustry.entities.pattern.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.gen.*;
@ -72,6 +73,8 @@ public class BulletType extends Content implements Cloneable{
public Effect despawnEffect = Fx.hitBulletSmall;
/** Effect created when shooting. */
public Effect shootEffect = Fx.shootSmall;
/** Pattern used to shoot this bullet. If null, uses turret's default pattern. */
public @Nullable ShootPattern shootPattern = null;
/** Effect created when charging starts; only usable in single-shot weapons with a firstShotDelay / shotDelay. */
public Effect chargeEffect = Fx.none;
/** Extra smoke effect created when shooting. */

View file

@ -67,7 +67,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
}
payloads.clear();
}
public void destroy(){
if(Vars.state.rules.unitPayloadsExplode) payloads.each(Payload::destroyed);
}
@ -77,7 +77,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
}
boolean canPickup(Unit unit){
return type.pickupUnits && payloadUsed() + unit.hitSize * unit.hitSize <= type.payloadCapacity + 0.001f && unit.team == team() && unit.isAI();
return type.pickupUnits && payloadUsed() + unit.hitSize * unit.hitSize <= type.payloadCapacity + 0.001f && unit.team == team() && unit.isAI() && unit.type.allowedInPayloads;
}
boolean canPickup(Building build){

View file

@ -59,7 +59,8 @@ public class Schematics implements Loadable{
private ObjectSet<Schematic> errored = new ObjectSet<>();
private ObjectMap<CoreBlock, Seq<Schematic>> loadouts = new ObjectMap<>();
private ObjectMap<CoreBlock, Schematic> defaultLoadouts = new ObjectMap<>();
private FrameBuffer shadowBuffer;
private @Nullable FrameBuffer shadowBuffer;
private boolean triedCreatingShadowBuffer;
private Texture errorTexture;
private long lastClearTime;
@ -97,10 +98,6 @@ public class Schematics implements Loadable{
});
all.sort();
if(shadowBuffer == null && !headless){
Core.app.post(() -> shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8));
}
}
private void loadLoadouts(){
@ -215,35 +212,49 @@ public class Schematics implements Loadable{
Tmp.m2.set(Draw.trans());
FrameBuffer buffer = new FrameBuffer((schematic.width + padding) * resolution, (schematic.height + padding) * resolution);
shadowBuffer.begin(Color.clear);
Draw.trans().idt();
Draw.proj().setOrtho(0, 0, shadowBuffer.getWidth(), shadowBuffer.getHeight());
Draw.color();
schematic.tiles.each(t -> {
int size = t.block.size;
int offsetx = -(size - 1) / 2;
int offsety = -(size - 1) / 2;
for(int dx = 0; dx < size; dx++){
for(int dy = 0; dy < size; dy++){
int wx = t.x + dx + offsetx;
int wy = t.y + dy + offsety;
Fill.square(padding/2f + wx + 0.5f, padding/2f + wy + 0.5f, 0.5f);
}
if(shadowBuffer == null && !triedCreatingShadowBuffer){
triedCreatingShadowBuffer = true;
try{
shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8);
}catch(Exception e){
Log.err(Strings.format("Failed to create shadow buffer (@x@): @. This is likely because a mod is setting maxSchematicSize too high. Don't do that.",
maxSchematicSize + padding + 8, maxSchematicSize + padding + 8, Strings.getSimpleMessage(e)));
}
});
}
shadowBuffer.end();
if(shadowBuffer != null){
shadowBuffer.begin(Color.clear);
Draw.trans().idt();
Draw.proj().setOrtho(0, 0, shadowBuffer.getWidth(), shadowBuffer.getHeight());
Draw.color();
schematic.tiles.each(t -> {
int size = t.block.size;
int offsetx = -(size - 1) / 2;
int offsety = -(size - 1) / 2;
for(int dx = 0; dx < size; dx++){
for(int dy = 0; dy < size; dy++){
int wx = t.x + dx + offsetx;
int wy = t.y + dy + offsety;
Fill.square(padding/2f + wx + 0.5f, padding/2f + wy + 0.5f, 0.5f);
}
}
});
shadowBuffer.end();
}
buffer.begin(Color.clear);
Draw.proj().setOrtho(0, buffer.getHeight(), buffer.getWidth(), -buffer.getHeight());
Tmp.tr1.set(shadowBuffer.getTexture(), 0, 0, schematic.width + padding, schematic.height + padding);
Draw.color(0f, 0f, 0f, 1f);
Draw.rect(Tmp.tr1, buffer.getWidth()/2f, buffer.getHeight()/2f, buffer.getWidth(), -buffer.getHeight());
Draw.color();
if(shadowBuffer != null){
Tmp.tr1.set(shadowBuffer.getTexture(), 0, 0, schematic.width + padding, schematic.height + padding);
Draw.color(0f, 0f, 0f, 1f);
Draw.rect(Tmp.tr1, buffer.getWidth()/2f, buffer.getHeight()/2f, buffer.getWidth(), -buffer.getHeight());
Draw.color();
}
Seq<BuildPlan> plans = schematic.tiles.map(t -> new BuildPlan(t.x, t.y, t.rotation, t.block, t.config));
@ -473,10 +484,6 @@ public class Schematics implements Loadable{
if(check && !(st.block instanceof CoreBlock)){
seq.clear();
tile.getLinkedTilesAs(st.block, seq);
//remove env blocks, or not?
//if(seq.contains(t -> !t.block().alwaysReplace && !t.synthetic())){
// return;
//}
for(var t : seq){
if(t.block() != Blocks.air){
t.remove();

View file

@ -20,6 +20,7 @@ public class ClassMap{
classes.put("LogicAI", mindustry.ai.types.LogicAI.class);
classes.put("MinerAI", mindustry.ai.types.MinerAI.class);
classes.put("MissileAI", mindustry.ai.types.MissileAI.class);
classes.put("PrebuildAI", mindustry.ai.types.PrebuildAI.class);
classes.put("RepairAI", mindustry.ai.types.RepairAI.class);
classes.put("SuicideAI", mindustry.ai.types.SuicideAI.class);
classes.put("Ability", mindustry.entities.abilities.Ability.class);
@ -112,6 +113,7 @@ public class ClassMap{
classes.put("PayloadSeq", mindustry.type.PayloadSeq.class);
classes.put("PayloadStack", mindustry.type.PayloadStack.class);
classes.put("Planet", mindustry.type.Planet.class);
classes.put("PlanetData", mindustry.type.Planet.PlanetData.class);
classes.put("Publishable", mindustry.type.Publishable.class);
classes.put("Sector", mindustry.type.Sector.class);
classes.put("SectorRect", mindustry.type.Sector.SectorRect.class);
@ -151,6 +153,7 @@ public class ClassMap{
classes.put("ItemSelection", mindustry.world.blocks.ItemSelection.class);
classes.put("LaunchAnimator", mindustry.world.blocks.LaunchAnimator.class);
classes.put("RotBlock", mindustry.world.blocks.RotBlock.class);
classes.put("TileBitmask", mindustry.world.blocks.TileBitmask.class);
classes.put("UnitTetherBlock", mindustry.world.blocks.UnitTetherBlock.class);
classes.put("Accelerator", mindustry.world.blocks.campaign.Accelerator.class);
classes.put("AcceleratorBuild", mindustry.world.blocks.campaign.Accelerator.AcceleratorBuild.class);
@ -257,7 +260,10 @@ public class ClassMap{
classes.put("StackRouter", mindustry.world.blocks.distribution.StackRouter.class);
classes.put("StackRouterBuild", mindustry.world.blocks.distribution.StackRouter.StackRouterBuild.class);
classes.put("AirBlock", mindustry.world.blocks.environment.AirBlock.class);
classes.put("CharacterOverlay", mindustry.world.blocks.environment.CharacterOverlay.class);
classes.put("Cliff", mindustry.world.blocks.environment.Cliff.class);
classes.put("ColoredFloor", mindustry.world.blocks.environment.ColoredFloor.class);
classes.put("ColoredWall", mindustry.world.blocks.environment.ColoredWall.class);
classes.put("EmptyFloor", mindustry.world.blocks.environment.EmptyFloor.class);
classes.put("Floor", mindustry.world.blocks.environment.Floor.class);
classes.put("UpdateRenderState", mindustry.world.blocks.environment.Floor.UpdateRenderState.class);
@ -266,6 +272,7 @@ public class ClassMap{
classes.put("Prop", mindustry.world.blocks.environment.Prop.class);
classes.put("RemoveOre", mindustry.world.blocks.environment.RemoveOre.class);
classes.put("RemoveWall", mindustry.world.blocks.environment.RemoveWall.class);
classes.put("RuneOverlay", mindustry.world.blocks.environment.RuneOverlay.class);
classes.put("SeaBush", mindustry.world.blocks.environment.SeaBush.class);
classes.put("Seaweed", mindustry.world.blocks.environment.Seaweed.class);
classes.put("ShallowLiquid", mindustry.world.blocks.environment.ShallowLiquid.class);
@ -315,6 +322,8 @@ public class ClassMap{
classes.put("MessageBuild", mindustry.world.blocks.logic.MessageBlock.MessageBuild.class);
classes.put("SwitchBlock", mindustry.world.blocks.logic.SwitchBlock.class);
classes.put("SwitchBuild", mindustry.world.blocks.logic.SwitchBlock.SwitchBuild.class);
classes.put("TileableLogicDisplay", mindustry.world.blocks.logic.TileableLogicDisplay.class);
classes.put("TileableLogicDisplayBuild", mindustry.world.blocks.logic.TileableLogicDisplay.TileableLogicDisplayBuild.class);
classes.put("BlockProducer", mindustry.world.blocks.payloads.BlockProducer.class);
classes.put("BlockProducerBuild", mindustry.world.blocks.payloads.BlockProducer.BlockProducerBuild.class);
classes.put("BuildPayload", mindustry.world.blocks.payloads.BuildPayload.class);

View file

@ -5,6 +5,7 @@ import arc.graphics.g2d.*;
import arc.math.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
import mindustry.gen.*;
@ -64,7 +65,7 @@ public class ShockMine extends Block{
public void triggered(){
for(int i = 0; i < tendrils; i++){
Lightning.create(team, lightningColor, damage, x, y, Mathf.random(360f), length);
Lightning.create(Bullets.damageLightningGround, team, lightningColor, damage, x, y, Mathf.random(360f), length);
}
if(bullet != null){
for(int i = 0; i < shots; i++){

View file

@ -581,6 +581,14 @@ public class Turret extends ReloadTurret{
super.handleLiquid(source, liquid, amount);
}
@Override
public boolean canConsume(){
if(heatRequirement > 0 && heatReq <= 0f){
return false;
}
return super.canConsume();
}
protected boolean validateTarget(){
return !Units.invalidateTarget(target, canHeal() ? Team.derelict : team, x, y) || isControlled() || logicControlled();
}
@ -697,7 +705,9 @@ public class Turret extends ReloadTurret{
type.chargeEffect.at(bulletX, bulletY, rotation);
}
shoot.shoot(barrelCounter, (xOffset, yOffset, angle, delay, mover) -> {
ShootPattern pattern = type.shootPattern != null ? type.shootPattern : shoot;
pattern.shoot(barrelCounter, (xOffset, yOffset, angle, delay, mover) -> {
queuedBullets++;
int barrel = barrelCounter;