Compare commits

...

14 Commits
v141 ... v141.2

Author SHA1 Message Date
Anuken
00d6b0d483 Merge remote-tracking branch 'origin/master' 2023-01-13 09:59:05 -05:00
Anuken
598049265a Complete revert of #8019 2023-01-13 09:59:01 -05:00
Nunting
c3209f2cdd Update bundle_ko.properties (#8171) 2023-01-13 09:44:08 -05:00
Anuken
b2b4602c91 Reverting #8037 2023-01-13 09:42:28 -05:00
Anuken
b1e1fe4922 Merge remote-tracking branch 'origin/master' 2023-01-13 09:31:14 -05:00
Anuken
edcf3c4223 Fixed #8170 2023-01-13 09:31:10 -05:00
Github Actions
58315acfa3 Automatic bundle update 2023-01-13 03:53:33 +00:00
Anuken
ec9eadd660 encoding test 2023-01-12 22:52:32 -05:00
Anuken
7277892e33 Merge remote-tracking branch 'origin/master' 2023-01-12 17:01:52 -05:00
Anuken
54962a158d Fixed #8168 2023-01-12 17:01:47 -05:00
Даркнесс#3729
30dcbe1af3 Administration improvements (#8167)
* Administration improvements

Reduced the interval for preventing sending the same message because 50 seconds is too much and kinda confusing
Added saving and loading kicked IPs so they are saved even when the server crashes
Fixed smth in Config

* skill issue
2023-01-12 16:24:51 -05:00
Anuken
e92d5d2d2d Fixed #8165 2023-01-12 14:42:42 -05:00
Anuken
411367f294 Revert JSON rules change 2023-01-12 14:38:11 -05:00
Anuken
f49e05a915 Fixed #8161 / Fixed #8162 2023-01-12 14:30:57 -05:00
12 changed files with 1051 additions and 1135 deletions

View File

@@ -629,7 +629,7 @@ public class EntityProcess extends BaseProcessor{
groupsBuilder.addField(ParameterizedTypeName.get( groupsBuilder.addField(ParameterizedTypeName.get(
ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC); ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC);
groupInit.addStatement("$L = new $T<>($L.class, $L, $L, (e, pos) -> (($L.IndexableEntity__$L)e).setIndex__$L(pos))", group.name, groupc, itype, group.spatial, group.mapping, packageName, group.name, group.name); groupInit.addStatement("$L = new $T<>($L.class, $L, $L, (e, pos) -> { if(e instanceof $L.IndexableEntity__$L ix) ix.setIndex__$L(pos); })", group.name, groupc, itype, group.spatial, group.mapping, packageName, group.name, group.name);
} }
//write the groups //write the groups

File diff suppressed because it is too large Load Diff

View File

@@ -288,7 +288,7 @@ server.invalidport = 잘못된 포트 번호입니다!
server.error = [scarlet]서버 호스팅 오류 server.error = [scarlet]서버 호스팅 오류
save.new = 새로 저장 save.new = 새로 저장
save.overwrite = 저장된 슬롯을 덮어쓰시겠습니까? save.overwrite = 저장된 슬롯을 덮어쓰시겠습니까?
save.nocampaign = Individual save files from the campaign cannot be imported. save.nocampaign = 캠페인의 개별 저장 파일을 가져올 수 없습니다.
overwrite = 덮어쓰기 overwrite = 덮어쓰기
save.none = 저장된 파일을 찾을 수 없습니다! save.none = 저장된 파일을 찾을 수 없습니다!
savefail = 게임을 저장하지 못했습니다! savefail = 게임을 저장하지 못했습니다!
@@ -356,8 +356,8 @@ pausebuilding = [accent][[{0}][] 키를 눌러 건설을 일시중지
resumebuilding = [scarlet][[{0}][] 키를 눌러 건설을 재개 resumebuilding = [scarlet][[{0}][] 키를 눌러 건설을 재개
enablebuilding = [scarlet][[{0}][] 키를 눌러 건설을 활성 enablebuilding = [scarlet][[{0}][] 키를 눌러 건설을 활성
showui = UI가 숨겨졌습니다. [accent][[{0}][] 키를 눌러 UI를 활성화하세요. showui = UI가 숨겨졌습니다. [accent][[{0}][] 키를 눌러 UI를 활성화하세요.
commandmode.name = [accent]Command Mode commandmode.name = [accent]명령 모드
commandmode.nounits = [no units] commandmode.nounits = [기체 없음]
wave = [accent]{0}단계 wave = [accent]{0}단계
wave.cap = [accent]단계 {0}/{1} wave.cap = [accent]단계 {0}/{1}
wave.waiting = 다음 단계까지[lightgray] {0}초 wave.waiting = 다음 단계까지[lightgray] {0}초
@@ -441,7 +441,7 @@ waves.max = 기까지
waves.guardian = 수호자 waves.guardian = 수호자
waves.preview = 미리보기 waves.preview = 미리보기
waves.edit = 편집 waves.edit = 편집
waves.random = Random waves.random = 무작위
waves.copy = 클립보드로 복사하기 waves.copy = 클립보드로 복사하기
waves.load = 클립보드에서 불러오기 waves.load = 클립보드에서 불러오기
waves.invalid = 클립보드에 잘못된 단계 데이터가 있습니다. waves.invalid = 클립보드에 잘못된 단계 데이터가 있습니다.
@@ -700,7 +700,7 @@ sectors.enemybase = [scarlet]적 기지[]
sectors.vulnerable = [scarlet]취약함[] sectors.vulnerable = [scarlet]취약함[]
sectors.underattack = [scarlet]공격받고 있습니다! [accent]{0}% 손상됨[] sectors.underattack = [scarlet]공격받고 있습니다! [accent]{0}% 손상됨[]
sectors.underattack.nodamage = [scarlet]점령되지 않음 sectors.underattack.nodamage = [scarlet]점령되지 않음
sectors.survives = [accent]{0} 단계 이상 버티세요.[] sectors.survives = [accent]{0} 단계 버팀[]
sectors.go = 진입 sectors.go = 진입
sector.abandon = 포기 sector.abandon = 포기
sector.abandon.confirm = 이 지역의 코어가 자폭합니다.\n계속하시겠습니까? sector.abandon.confirm = 이 지역의 코어가 자폭합니다.\n계속하시겠습니까?
@@ -1205,7 +1205,7 @@ rules.unitbuildspeedmultiplier = 기체 생산속도 배수
rules.unitcostmultiplier = 기체 비용 배수 rules.unitcostmultiplier = 기체 비용 배수
rules.unithealthmultiplier = 기체 체력 배수 rules.unithealthmultiplier = 기체 체력 배수
rules.unitdamagemultiplier = 기체 피해량 배수 rules.unitdamagemultiplier = 기체 피해량 배수
rules.unitcrashdamagemultiplier = Unit Crash Damage Multiplier rules.unitcrashdamagemultiplier = 기체 파손 피해량 배수
rules.solarmultiplier = 태양광 전력 배수 rules.solarmultiplier = 태양광 전력 배수
rules.unitcapvariable = 코어 기체수 제한 추가 rules.unitcapvariable = 코어 기체수 제한 추가
rules.unitcap = 기본 기체 제한 rules.unitcap = 기본 기체 제한

View File

@@ -1277,6 +1277,7 @@ public class Blocks{
heatOutput = 3f; heatOutput = 3f;
regionRotated1 = 1; regionRotated1 = 1;
ambientSound = Sounds.hum; ambientSound = Sounds.hum;
itemCapacity = 0;
consumePower(100f / 60f); consumePower(100f / 60f);
}}; }};
@@ -1287,6 +1288,7 @@ public class Blocks{
drawer = new DrawMulti(new DrawRegion("-bottom"), new DrawLiquidTile(Liquids.slag), new DrawDefault(), new DrawHeatOutput()); drawer = new DrawMulti(new DrawRegion("-bottom"), new DrawLiquidTile(Liquids.slag), new DrawDefault(), new DrawHeatOutput());
size = 3; size = 3;
itemCapacity = 0;
liquidCapacity = 40f; liquidCapacity = 40f;
rotateDraw = false; rotateDraw = false;
regionRotated1 = 1; regionRotated1 = 1;

View File

@@ -6,7 +6,6 @@ import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.pooling.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
@@ -27,8 +26,6 @@ public class Damage{
private static final Seq<Unit> units = new Seq<>(); private static final Seq<Unit> units = new Seq<>();
private static final IntSet collidedBlocks = new IntSet(); private static final IntSet collidedBlocks = new IntSet();
private static final IntFloatMap damages = new IntFloatMap(); private static final IntFloatMap damages = new IntFloatMap();
private static final Seq<Collided> collided = new Seq<>();
private static final Pool<Collided> collidePool = Pools.get(Collided.class, Collided::new);
private static final Seq<Building> builds = new Seq<>(); private static final Seq<Building> builds = new Seq<>();
private static final FloatSeq distances = new FloatSeq(); private static final FloatSeq distances = new FloatSeq();
@@ -187,7 +184,7 @@ public class Damage{
return Math.min(distances.size < pierceCap || pierceCap < 0 ? length : Math.max(6f, distances.get(pierceCap - 1)), maxDst); return Math.min(distances.size < pierceCap || pierceCap < 0 ? length : Math.max(6f, distances.get(pierceCap - 1)), maxDst);
} }
/** Collides a bullet with blocks in a laser, taking into account absorption blocks. Resulting length is stored in the bullet's fdata. */ /** TODO Collides a bullet with blocks in a laser, taking into account absorption blocks. Resulting length is stored in the bullet's fdata. */
public static float collideLaser(Bullet b, float length, boolean large, boolean laser, int pierceCap){ public static float collideLaser(Bullet b, float length, boolean large, boolean laser, int pierceCap){
float resultLength = findPierceLength(b, pierceCap, length); float resultLength = findPierceLength(b, pierceCap, length);
@@ -229,26 +226,40 @@ public class Damage{
length = findPierceLength(hitter, pierceCap, length); length = findPierceLength(hitter, pierceCap, length);
} }
//TODO pierceCount ++ should happen in blocks AND units!
collidedBlocks.clear(); collidedBlocks.clear();
vec.trnsExact(angle, length); vec.trnsExact(angle, length);
Intc2 collider = (cx, cy) -> {
Building tile = world.build(cx, cy);
boolean collide = tile != null && hitter.checkUnderBuild(tile, cx * tilesize, cy * tilesize) && collidedBlocks.add(tile.pos());
if(hitter.damage > 0){
float health = !collide ? 0 : tile.health;
if(collide && tile.team != team && tile.collide(hitter)){
tile.collision(hitter);
hitter.type.hit(hitter, cx * tilesize, cy * tilesize);
}
//try to heal the tile
if(collide && hitter.type.testCollision(hitter, tile)){
hitter.type.hitTile(hitter, tile, cx * tilesize, cy * tilesize, health, false);
}
}
};
if(hitter.type.collidesGround){ if(hitter.type.collidesGround){
seg1.set(x, y); seg1.set(x, y);
seg2.set(seg1).add(vec); seg2.set(seg1).add(vec);
World.raycastEachWorld(x, y, seg2.x, seg2.y, (cx, cy) -> { World.raycastEachWorld(x, y, seg2.x, seg2.y, (cx, cy) -> {
Building tile = world.build(cx, cy); collider.get(cx, cy);
boolean collide = tile != null && hitter.checkUnderBuild(tile, cx * tilesize, cy * tilesize) && collidedBlocks.add(tile.pos());
if(collide){
collided.add(collidePool.obtain().set(cx * tilesize, cy * tilesize, tile));
for(Point2 p : Geometry.d4){ for(Point2 p : Geometry.d4){
Tile other = world.tile(p.x + cx, p.y + cy); Tile other = world.tile(p.x + cx, p.y + cy);
if(other != null && (large || Intersector.intersectSegmentRectangle(seg1, seg2, other.getBounds(Tmp.r1)))){ if(other != null && (large || Intersector.intersectSegmentRectangle(seg1, seg2, other.getBounds(Tmp.r1)))){
Building build = other.build; collider.get(cx + p.x, cy + p.y);
if(build != null && hitter.checkUnderBuild(build, cx * tilesize, cy * tilesize) && collidedBlocks.add(build.pos())){
collided.add(collidePool.obtain().set((p.x + cx * tilesize), (p.y + cy) * tilesize, build));
}
}
} }
} }
return false; return false;
@@ -260,44 +271,32 @@ public class Damage{
rect.setPosition(x, y).setSize(vec.x, vec.y).normalize().grow(expand * 2f); rect.setPosition(x, y).setSize(vec.x, vec.y).normalize().grow(expand * 2f);
float x2 = vec.x + x, y2 = vec.y + y; float x2 = vec.x + x, y2 = vec.y + y;
Units.nearbyEnemies(team, rect, u -> { Cons<Unit> cons = e -> {
if(u.checkTarget(hitter.type.collidesAir, hitter.type.collidesGround) && u.hittable()){ if(!e.hittable()) return;
u.hitbox(hitrect); //the peirce cap works for units, but really terribly, I'm just disabling it for now.
//if(pierceCap > 0 && pierceCount > pierceCap) return;
e.hitbox(hitrect);
Vec2 vec = Geometry.raycastRect(x, y, x2, y2, hitrect.grow(expand * 2)); Vec2 vec = Geometry.raycastRect(x, y, x2, y2, hitrect.grow(expand * 2));
if(vec != null){ if(vec != null && hitter.damage > 0){
collided.add(collidePool.obtain().set(vec.x, vec.y, u)); effect.at(vec.x, vec.y);
e.collision(hitter, vec.x, vec.y);
hitter.collision(e, vec.x, vec.y);
} }
};
units.clear();
Units.nearbyEnemies(team, rect, u -> {
if(u.checkTarget(hitter.type.collidesAir, hitter.type.collidesGround)){
units.add(u);
} }
}); });
int[] collideCount = {0}; units.sort(u -> u.dst2(hitter));
collided.sort(c -> hitter.dst2(c.x, c.y)); units.each(cons);
collided.each(c -> {
if(hitter.damage > 0 && (pierceCap <= 0 || collideCount[0]++ < pierceCap)){
if(c.target instanceof Unit u){
effect.at(c.x, c.y);
u.collision(hitter, c.x, c.y);
hitter.collision(u, c.x, c.y);
}else if(c.target instanceof Building tile){
float health = tile.health;
if(tile.team != team && tile.collide(hitter)){
tile.collision(hitter);
hitter.type.hit(hitter, c.x, c.y);
}
//try to heal the tile
if(hitter.type.testCollision(hitter, tile)){
hitter.type.hitTile(hitter, tile, c.x, c.y, health, false);
}
}
}
});
collidePool.freeAll(collided);
collided.clear();
} }
/** /**
@@ -614,16 +613,4 @@ public class Damage{
public static float applyArmor(float damage, float armor){ public static float applyArmor(float damage, float armor){
return Math.max(damage - armor, minArmorDamage * damage); return Math.max(damage - armor, minArmorDamage * damage);
} }
public static class Collided{
public float x, y;
public Teamc target;
public Collided set(float x, float y, Teamc target){
this.x = x;
this.y = y;
this.target = target;
return this;
}
}
} }

View File

@@ -69,7 +69,7 @@ public class Puddles{
if(tile.floor().solid) return; if(tile.floor().solid) return;
Puddle p = map.get(tile.pos()); Puddle p = map.get(tile.pos());
if(p == null){ if(p == null || p.liquid == null){
Puddle puddle = Puddle.create(); Puddle puddle = Puddle.create();
puddle.tile = tile; puddle.tile = tile;
puddle.liquid = liquid; puddle.liquid = liquid;
@@ -107,6 +107,8 @@ public class Puddles{
/** Reacts two liquids together at a location. */ /** Reacts two liquids together at a location. */
private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){ private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){
if(dest == null) return 0f;
if((dest.flammability > 0.3f && liquid.temperature > 0.7f) || if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid (liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
Fires.create(tile); Fires.create(tile);

View File

@@ -142,6 +142,8 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc, Syncc{
@Override @Override
public void afterSync(){ public void afterSync(){
if(liquid != null){
Puddles.register(self()); Puddles.register(self());
} }
} }
}

View File

@@ -2,7 +2,6 @@ package mindustry.io;
import arc.graphics.*; import arc.graphics.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.serialization.*; import arc.util.serialization.*;
import arc.util.serialization.Json.*; import arc.util.serialization.Json.*;
@@ -79,30 +78,6 @@ public class JsonIO{
json.setElementType(Rules.class, "spawns", SpawnGroup.class); json.setElementType(Rules.class, "spawns", SpawnGroup.class);
json.setElementType(Rules.class, "loadout", ItemStack.class); json.setElementType(Rules.class, "loadout", ItemStack.class);
json.setSerializer(Rules.class, new Serializer<>(){
@Override
public void write(Json json, Rules object, Class knownType){
json.writeObjectStart();
json.writeFields(object);
json.writeValue("envMapping", Env.idToName, IntMap.class);
json.writeObjectEnd();
}
@Override
public Rules read(Json json, JsonValue jsonData, Class type){
Rules out = baseObject instanceof Rules rules ? rules : new Rules();
json.readFields(out, jsonData);
// Older Rules data doesn't have env mapping.
if(jsonData.has("envMapping")){
IntMap<String> idToName = json.readValue(IntMap.class, jsonData.get("envMapping"));
out.env = Env.remap(out.env, idToName);
}
return out;
}
});
json.setSerializer(Color.class, new Serializer<>(){ json.setSerializer(Color.class, new Serializer<>(){
@Override @Override
public void write(Json json, Color object, Class knownType){ public void write(Json json, Color object, Class knownType){
@@ -345,6 +320,8 @@ public class JsonIO{
} }
}); });
//use short names for all filter types //use short names for all filter types
for(var filter : Maps.allFilterTypes){ for(var filter : Maps.allFilterTypes){
var i = filter.get(); var i = filter.get();
@@ -352,9 +329,8 @@ public class JsonIO{
} }
} }
private static Object baseObject;
static class CustomJson extends Json{ static class CustomJson extends Json{
private Object baseObject;
{ apply(this); } { apply(this); }
@@ -364,7 +340,7 @@ public class JsonIO{
} }
public <T> T fromBaseJson(Class<T> type, T base, String json){ public <T> T fromBaseJson(Class<T> type, T base, String json){
baseObject = base; this.baseObject = base;
return readValue(type, null, new JsonReader().parse(json)); return readValue(type, null, new JsonReader().parse(json));
} }

View File

@@ -48,8 +48,8 @@ public class Administration{
player.getInfo().messageInfractions = 0; player.getInfo().messageInfractions = 0;
} }
//prevent players from sending the same message twice in the span of 50 seconds //prevent players from sending the same message twice in the span of 10 seconds
if(message.equals(player.getInfo().lastSentMessage) && Time.timeSinceMillis(player.getInfo().lastMessageTime) < 1000 * 50){ if(message.equals(player.getInfo().lastSentMessage) && Time.timeSinceMillis(player.getInfo().lastMessageTime) < 1000 * 10){
player.sendMessage("[scarlet]You may not send the same message twice."); player.sendMessage("[scarlet]You may not send the same message twice.");
return null; return null;
} }
@@ -449,6 +449,7 @@ public class Administration{
public void save(){ public void save(){
Core.settings.putJson("player-data", playerInfo); Core.settings.putJson("player-data", playerInfo);
Core.settings.putJson("ip-kicks", kickedIPs);
Core.settings.putJson("ip-bans", String.class, bannedIPs); Core.settings.putJson("ip-bans", String.class, bannedIPs);
Core.settings.putJson("whitelist-ids", String.class, whitelist); Core.settings.putJson("whitelist-ids", String.class, whitelist);
Core.settings.putJson("banned-subnets", String.class, subnetBans); Core.settings.putJson("banned-subnets", String.class, subnetBans);
@@ -458,6 +459,7 @@ public class Administration{
private void load(){ private void load(){
//load default data //load default data
playerInfo = Core.settings.getJson("player-data", ObjectMap.class, ObjectMap::new); playerInfo = Core.settings.getJson("player-data", ObjectMap.class, ObjectMap::new);
kickedIPs = Core.settings.getJson("ip-kicks", ObjectMap.class, ObjectMap::new);
bannedIPs = Core.settings.getJson("ip-bans", Seq.class, Seq::new); bannedIPs = Core.settings.getJson("ip-bans", Seq.class, Seq::new);
whitelist = Core.settings.getJson("whitelist-ids", Seq.class, Seq::new); whitelist = Core.settings.getJson("whitelist-ids", Seq.class, Seq::new);
subnetBans = Core.settings.getJson("banned-subnets", Seq.class, Seq::new); subnetBans = Core.settings.getJson("banned-subnets", Seq.class, Seq::new);
@@ -488,7 +490,7 @@ public class Administration{
messageRateLimit = new Config("messageRateLimit", "Message rate limit in seconds. 0 to disable.", 0), messageRateLimit = new Config("messageRateLimit", "Message rate limit in seconds. 0 to disable.", 0),
messageSpamKick = new Config("messageSpamKick", "How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3), messageSpamKick = new Config("messageSpamKick", "How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3),
packetSpamLimit = new Config("packetSpamLimit", "Limit for packet count sent within 3sec that will lead to a blacklist + kick.", 300), packetSpamLimit = new Config("packetSpamLimit", "Limit for packet count sent within 3sec that will lead to a blacklist + kick.", 300),
chatSpamLimit = new Config("packetSpamLimit", "Limit for chat packet count sent within 2sec that will lead to a blacklist + kick. Not the same as a rate limit.", 20), chatSpamLimit = new Config("chatSpamLimit", "Limit for chat packet count sent within 2sec that will lead to a blacklist + kick. Not the same as a rate limit.", 20),
socketInput = new Config("socketInput", "Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)), socketInput = new Config("socketInput", "Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
socketInputPort = new Config("socketInputPort", "The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)), socketInputPort = new Config("socketInputPort", "The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
socketInputAddress = new Config("socketInputAddress", "The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)), socketInputAddress = new Config("socketInputAddress", "The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),

View File

@@ -261,7 +261,7 @@ public class ArcNetProvider implements NetProvider{
} }
ByteBuffer buffer = ByteBuffer.wrap(packet.getData()); ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer); Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer);
callback.get(host); Core.app.post(() -> callback.get(host));
foundAddresses.add(packet.getAddress()); foundAddresses.add(packet.getAddress());
}catch(Exception e){ }catch(Exception e){
//don't crash when there's an error pinging a server or parsing data //don't crash when there's an error pinging a server or parsing data

View File

@@ -61,7 +61,9 @@ public class GenericCrafter extends Block{
public void setStats(){ public void setStats(){
stats.timePeriod = craftTime; stats.timePeriod = craftTime;
super.setStats(); super.setStats();
if((hasItems && itemCapacity > 0) || outputItems != null){
stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds); stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds);
}
if(outputItems != null){ if(outputItems != null){
stats.add(Stat.output, StatValues.items(craftTime, outputItems)); stats.add(Stat.output, StatValues.items(craftTime, outputItems));

View File

@@ -1,83 +1,26 @@
package mindustry.world.meta; package mindustry.world.meta;
import arc.struct.*;
import arc.util.*;
/** Environmental flags for different types of locations. */ /** Environmental flags for different types of locations. */
public class Env{ public class Env{
public static final int public static final int
//is on a planet //is on a planet
terrestrial, terrestrial = 1,
//is in space, no atmosphere //is in space, no atmosphere
space, space = 1 << 1,
//is underwater, on a planet //is underwater, on a planet
underwater, underwater = 1 << 2,
//has a spores //has a spores
spores, spores = 1 << 3,
//has a scorching env effect //has a scorching env effect
scorching, scorching = 1 << 4,
//has oil reservoirs //has oil reservoirs
groundOil, groundOil = 1 << 5,
//has water reservoirs //has water reservoirs
groundWater, groundWater = 1 << 6,
//has oxygen in the atmosphere //has oxygen in the atmosphere
oxygen, oxygen = 1 << 7,
//all attributes combined, only used for bitmasking purposes //all attributes combined, only used for bitmasking purposes
any = 0xffffffff, any = 0xffffffff,
//no attributes (0) //no attributes (0)
none = 0; none = 0;
//do NOT modify directly!
public static final ObjectIntMap<String> nameToId;
public static final IntMap<String> idToName;
static{
//last time i didn't use a static initializer i got a null pointer.
//i can probably just move the fields around, but i don't trust java enough for that
nameToId = new ObjectIntMap<>();
idToName = new IntMap<>();
terrestrial = add("terrestrial");
space = add("space");
underwater = add("underwater");
spores = add("spores");
scorching = add("scorching");
groundOil = add("groundOil");
groundWater = add("groundWater");
oxygen = add("oxygen");
}
public static int add(String key){
if(nameToId.containsKey(key)) throw new IllegalArgumentException("'" + key + "' env already exists.");
if(nameToId.size >= 32) throw new IllegalStateException("Max env count 32 exceeded.");
int id = 1 << nameToId.size;
nameToId.put(key, id);
idToName.put(id, key);
return id;
}
public static int remap(int mask, IntMap<String> idToName){
int out = 0;
for(int i = 0; i < 32; i++){
int key = 1 << i;
if((mask & key) == key){
String name = idToName.get(key);
if(name == null){
//if it's unmapped it's probably just some mods using constant value
out |= key;
continue;
}
int id = nameToId.get(name, -1);
if(id == -1){
Log.warn("Ignoring '@' env key.", name);
}else{
out |= id;
}
}
}
return out;
}
} }