Merge branch 'master' into crater

This commit is contained in:
Patrick 'Quezler' Mounier
2019-12-29 19:09:27 +01:00
39 changed files with 527 additions and 260 deletions

View File

@@ -29,6 +29,13 @@ load.system = System
load.mod = Mods
load.scripts = Scripts
be.update = A new Bleeding Edge build is available:
be.update.confirm = Download it and restart now?
be.updating = Updating...
be.ignore = Ignore
be.noupdates = No updates found.
be.check = Check for updates
schematic = Schematic
schematic.add = Save Schematic...
schematics = Schematics
@@ -148,6 +155,7 @@ server.kicked.nameEmpty = Your chosen name is invalid.
server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted.
server.kicked.customClient = This server does not support custom builds. Download an official version.
server.kicked.gameover = Game over!
server.kicked.serverRestarting = The server is restarting.
server.versions = Your version:[accent] {0}[]\nServer version:[accent] {1}[]
host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [lightgray]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[lightgray]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. Note that public networks sometimes do not allow server discovery.
join.info = Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[lightgray]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP.
@@ -635,6 +643,7 @@ setting.screenshake.name = Screen Shake
setting.effects.name = Display Effects
setting.destroyedblocks.name = Display Destroyed Blocks
setting.conveyorpathfinding.name = Conveyor Placement Pathfinding
setting.coreselect.name = Allow Schematic Cores
setting.sensitivity.name = Controller Sensitivity
setting.saveinterval.name = Save Interval
setting.seconds = {0} seconds

View File

@@ -7,14 +7,15 @@ link.reddit.description = Mindustry 레딧
link.github.description = 게임 소스코드
link.changelog.description = 새로 추가된 것들
link.dev-builds.description = 불안정한 개발 빌드들
link.trello.description = 다음 출시될 기능들을 게시한 공식 Trello 보드
link.trello.description = 출시 예정중인 기능들을 게시한 공식 Trello 보드
link.itch.io.description = PC 버전 다운로드와 HTML5 버전이 있는 itch.io 사이트
link.google-play.description = Google Play 스토어 정보
link.f-droid.description = F-Droid 카탈로그
link.wiki.description = 공식 Mindustry 위키
link.feathub.description = 기능 아이디어 건의하기
linkfail = 링크를 여는 데 실패했습니다!\nURL이 기기의 클립보드에 복사되었습니다.
screenshot = 스크린샷이 {0} 경로에 저장되었습니다.
screenshot.invalid = 맵이 너무 커서 스크린샷을 찍을 메모리가 충분하지 않습니다.
screenshot = 스크린 샷이 {0} 경로에 저장되었습니다.
screenshot.invalid = 맵이 너무 커서 스크린 샷을 찍을 메모리가 충분하지 않습니다.
gameover = 게임 오버
gameover.pvp = [accent]{0}[] 팀이 승리했습니다!
highscore = [accent]최고점수 달성!
@@ -35,11 +36,11 @@ schematic.replace = 이 설계도와 같은 이름의 설계도가 이미 존재
schematic.import = 설계도 불러오기
schematic.exportfile = 파일 내보내기
schematic.importfile = 파일 불러오기
schematic.browseworkshop = 워크샵 탐색
schematic.browseworkshop = Workshop 탐색
schematic.copy = 클립보드에 복사하기
schematic.copy.import = 클립보드에서 붙여넣기
schematic.shareworkshop = 워크샵에 공유
schematic.flip = 좌우 뒤집기 :[accent][[{0}][] / 상하 뒤집기 : [accent][[{1}][]
schematic.flip = 좌우 뒤집기 : [accent][[{0}][] / 상하 뒤집기 : [accent][[{1}][]
schematic.saved = 설계도 저장됨.
schematic.delete.confirm = 삭제된 설계도는 복구할 수 없습니다. 정말로 삭제하시겠습니까?
schematic.rename = 설계도명 변경
@@ -94,7 +95,7 @@ mods.alpha = [scarlet](Alpha)
mods = 모드
mods.none = [LIGHT_GRAY]추가한 모드가 없습니다!
mods.guide = 모드 가이드
mods.report = 버그 신고
mods.report = 문제 신고
mods.openfolder = 모드 폴더 열기
mod.enabled = [lightgray]활성화
mod.disabled = [scarlet]비활성화
@@ -102,7 +103,10 @@ mod.disable = 비활성화
mod.delete.error = 모드를 삭제할 수 없습니다. 아마도 해당 모드가 사용중인 것 같습니다.
mod.requiresversion = [scarlet]게임의 버전이 낮아 모드를 활성화할 수 없습니다!\n[scarlet]요구되는 게임 버전 : [accent]{0}
mod.missingdependencies = [scarlet]의존되는 모드: {0}
mod.nowdisabled = [scarlet]모드 '{0}'는 다음의 모드에 의존합니다 :[accent] {1}\n[lightgray]이 모드를 먼저 다운로드해야합니다.\n이 모드는 자동으로 비활성화됩니다.
mod.erroredcontent = [scarlet]컨텐츠 오류
mod.errors = 컨텐츠를 불러오는 중 오류가 발생하였습니다.
mod.noerrorplay = [scarlet]모드에 오류가 존재합니다.[] 해당 오류가 발생하는 모드를 비활성화하거나 모드의 오류를 고친 후 플레이가 가능합니다.
mod.nowdisabled = [scarlet]모드 '{0}'는 다음의 모드에 의존합니다 : [accent] {1}\n[lightgray]이 모드를 먼저 다운로드해야합니다.\n이 모드는 자동으로 비활성화됩니다.
mod.enable = 활성화
mod.requiresrestart = 모드 변경사항을 적용하기 위해 게임을 종료합니다.
mod.reloadrequired = [scarlet]새로고침 예정됨
@@ -111,7 +115,7 @@ mod.import.github = 깃허브 모드 추가
mod.item.remove = 이것은 모드[accent] '{0}'[]의 자원입니다. 이 자원을 삭제하려면, 이 모드를 제거해야합니다.
mod.remove.confirm = 이 모드를 삭제하시겠습니까?
mod.author = [LIGHT_GRAY]제작자 : [] {0}
mod.missing = 이 세이브파일에는 설치하지 않은 모드 혹은 버전에 속해있지 않은 데이터가 포함되어 있습니다. 이 파일을 불러올 경우 세이브파일의 데이터가 손상될 수 있습니다. 정말로 이 파일을 불러오시겠습니까?\n[lightgray]모드 :\n{0}
mod.missing = 이 세이브파일에는 설치하지 않은 모드 혹은 현재 버전에 속해있지 않은 데이터가 포함되어 있습니다. 이 파일을 불러올 경우 세이브파일의 데이터가 손상될 수 있습니다. 정말로 이 파일을 불러오시겠습니까?\n[lightgray]모드 :\n{0}
mod.preview.missing = 워크샵에 당신의 모드를 업로드하기 전에 미리보기 이미지를 먼저 추가해야합니다.\n[accent] preview.png[]라는 이름으로 미리보기 이미지를 당신의 모드 폴더안에 준비한 후 다시 시도해주세요.
mod.folder.missing = 워크샵에는 폴더 형태의 모드만 게시할 수 있습니다.\n모드를 폴더 형태로 바꾸려면 파일을 폴더에 압축 해제하고 이전 압축파일을 제거한 후, 게임을 재시작하거나 모드를 다시 로드하십시오.
mod.scripts.unsupported = 당신의 기기는 모드스크립트를 지원하지 않습니다. 모드의 일부 기능이 작동하지 않을 수 있습니다.
@@ -594,8 +598,8 @@ unit.persecond = /초
unit.timesspeed = x 배
unit.percent = %
unit.items = 자원
unit.thousands =
unit.millions = 백만
unit.thousands = k
unit.millions = mil
category.general = 일반
category.power = 전력
category.liquids = 액체
@@ -635,7 +639,7 @@ setting.sensitivity.name = 컨트롤러 감도
setting.saveinterval.name = 저장 간격
setting.seconds = {0} 초
setting.blockselecttimeout.name = 블록 선택 시간 초과
setting.milliseconds = {0} 밀리초
setting.milliseconds = {0} ms
setting.fullscreen.name = 전체 화면
setting.borderlesswindow.name = 테두리 없는 창모드[LIGHT_GRAY] (재시작이 필요할 수 있습니다)
setting.fps.name = FPS 표시
@@ -683,20 +687,20 @@ keybind.schematic_flip_x.name = 설계도 X축 뒤집기
keybind.schematic_flip_y.name = 설계도 Y축 뒤집기
keybind.category_prev.name = 이전 목록
keybind.category_next.name = 다음 목록
keybind.block_select_left.name = 왼쪽 선택
keybind.block_select_right.name = 오른쪽 선택
keybind.block_select_up.name = 위쪽 선택
keybind.block_select_down.name = 아래쪽 선택
keybind.block_select_01.name = 카테고리/블 선택 1
keybind.block_select_02.name = 카테고리/블 선택 2
keybind.block_select_03.name = 카테고리/블 선택 3
keybind.block_select_04.name = 카테고리/블 선택 4
keybind.block_select_05.name = 카테고리/블 선택 5
keybind.block_select_06.name = 카테고리/블 선택 6
keybind.block_select_07.name = 카테고리/블 선택 7
keybind.block_select_08.name = 카테고리/블 선택 8
keybind.block_select_09.name = 카테고리/블 선택 9
keybind.block_select_10.name = 카테고리/블 선택 10
keybind.block_select_left.name = 왼쪽 선택
keybind.block_select_right.name = 오른쪽 선택
keybind.block_select_up.name = 위쪽 선택
keybind.block_select_down.name = 아래쪽 선택
keybind.block_select_01.name = 카테고리/블 선택 1
keybind.block_select_02.name = 카테고리/블 선택 2
keybind.block_select_03.name = 카테고리/블 선택 3
keybind.block_select_04.name = 카테고리/블 선택 4
keybind.block_select_05.name = 카테고리/블 선택 5
keybind.block_select_06.name = 카테고리/블 선택 6
keybind.block_select_07.name = 카테고리/블 선택 7
keybind.block_select_08.name = 카테고리/블 선택 8
keybind.block_select_09.name = 카테고리/블 선택 9
keybind.block_select_10.name = 카테고리/블 선택 10
keybind.fullscreen.name = 전체 화면
keybind.select.name = 선택/공격
keybind.diagonal_placement.name = 대각선 설치
@@ -716,8 +720,8 @@ keybind.console.name = 콘솔
keybind.rotate.name = 회전
keybind.rotateplaced.name = 기존 회전 (고정)
keybind.toggle_menus.name = 메뉴 보이기/숨기기
keybind.chat_history_prev.name = 이전 채팅기록
keybind.chat_history_next.name = 다음 채팅기록
keybind.chat_history_prev.name = 이전 채팅 기록
keybind.chat_history_next.name = 다음 채팅 기록
keybind.chat_scroll.name = 채팅 스크롤
keybind.drop_unit.name = 유닛 처치 시 자원획득
keybind.zoom_minimap.name = 미니맵 확대
@@ -730,11 +734,11 @@ mode.editor.name = 편집기
mode.pvp.name = PvP
mode.pvp.description = 실제 플레이어와 PvP를 합니다. 맵에 적어도 2개의 다른 색상 코어가 있어야 합니다.
mode.attack.name = 공격
mode.attack.description = 적 기지를 파괴하세요. 맵에 빨간팀 코어가 있어야 플레이 가능합니다.
mode.attack.description = 적 기지를 파괴하세요. 맵에 빨간 팀 코어가 있어야 플레이 가능합니다.
mode.custom = 사용자 정의 규칙
rules.infiniteresources = 무한 자원
rules.reactorexplosions = 원자로 폭발 허가여부
rules.reactorexplosions = 원자로 폭발 허가 여부
rules.wavetimer = 단계 대기시간
rules.waves = 단계 활성화
rules.attack = 공격 모드
@@ -750,7 +754,7 @@ rules.respawntime = 플레이어 부활 대기 시간 : [LIGHT_GRAY] (초)
rules.wavespacing = 단계 간격 : [LIGHT_GRAY] (초)
rules.buildcostmultiplier = 건설 소모 배수
rules.buildspeedmultiplier = 건설 속도 배수
rules.waitForWaveToEnd = 단계가 끝날때까지 기다리는중
rules.waitForWaveToEnd = 단계가 끝날때까지 기다리는
rules.dropzoneradius = 소환 충격파 범위 : [LIGHT_GRAY] (타일)
rules.respawns = 단계당 최대 플레이어 부활 횟수
rules.limitedRespawns = 플레이어 부활 제한
@@ -810,7 +814,7 @@ mech.trident-ship.name = 트라이던트
mech.trident-ship.weapon = 폭탄 저장고
mech.glaive-ship.name = 글레이브
mech.glaive-ship.weapon = 중무장 인화성 소총
item.corestorable = [lightgray]코어 잔여 저장공간: {0}
item.corestorable = [lightgray]코어 저장 가능 여부 : {0}
item.explosiveness = [LIGHT_GRAY]폭발성 : {0}
item.flammability = [LIGHT_GRAY]인화성 : {0}
item.radioactivity = [LIGHT_GRAY]방사능 : {0}
@@ -1117,7 +1121,7 @@ block.cryofluidmixer.description = 물과 티타늄을 냉각에 훨씬 더 효
block.blast-mixer.description = 포자를 사용하여 파이라타이트를 폭발성 화합물로 변환시킵니다.
block.pyratite-mixer.description = 석탄, 납, 모래를 가연성이 높은 파이라타이트로 만듭니다.
block.melter.description = 고철을 녹여 파도의 탄약 혹은 원심 분리기에 사용할 수 있는 액체인 광재로 만듭니다.
block.separator.description = 광재 각종 자원으로 재활용 할 수 있게 해 주는 건물입니다.
block.separator.description = 광재 각종 자원으로 재활용 할 수 있게 해 주는 건물입니다.
block.spore-press.description = 포자를 압축해 기름을 추출합니다.
block.pulverizer.description = 고철을 갈아 모래로 만듭니다. 맵에 모래가 부족할 때 유용합니다.
block.coal-centrifuge.description = 석유로 석탄을 만듭니다.

View File

@@ -25,6 +25,7 @@ importPackage(Packages.arc.func)
importPackage(Packages.arc.graphics)
importPackage(Packages.arc.graphics.g2d)
importPackage(Packages.arc.math)
importPackage(Packages.arc.math.geom)
importPackage(Packages.arc.scene)
importPackage(Packages.arc.scene.actions)
importPackage(Packages.arc.scene.event)

View File

@@ -21,6 +21,7 @@ import mindustry.gen.*;
import mindustry.input.*;
import mindustry.maps.*;
import mindustry.mod.*;
import mindustry.net.*;
import mindustry.net.Net;
import mindustry.world.blocks.defense.ForceProjector.*;
@@ -53,8 +54,10 @@ public class Vars implements Loadable{
public static final String crashReportURL = "http://192.99.169.18/report";
/** URL the links to the wiki's modding guide.*/
public static final String modGuideURL = "https://mindustrygame.github.io/wiki/modding/";
/** URL to the JSON file containing all the global, public servers. */
/** URL to the JSON file containing all the global, public servers. Not queried in BE. */
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers.json";
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
/** URL the links to the wiki's modding guide.*/
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?template=bug_report.md";
/** list of built-in servers.*/
@@ -136,6 +139,8 @@ public class Vars implements Loadable{
public static Fi modDirectory;
/** data subdirectory used for schematics */
public static Fi schematicDirectory;
/** data subdirectory used for bleeding edge build versions */
public static Fi bebuildDirectory;
/** map file extension */
public static final String mapExtension = "msav";
/** save file extension */
@@ -157,6 +162,7 @@ public class Vars implements Loadable{
public static Platform platform = new Platform(){};
public static Mods mods;
public static Schematics schematics = new Schematics();
public static BeControl becontrol;
public static World world;
public static Maps maps;
@@ -220,6 +226,7 @@ public class Vars implements Loadable{
defaultWaves = new DefaultWaves();
collisions = new EntityCollisions();
world = new World();
becontrol = new BeControl();
maps = new Maps();
spawner = new WaveSpawner();
@@ -260,6 +267,7 @@ public class Vars implements Loadable{
tmpDirectory = dataDirectory.child("tmp/");
modDirectory = dataDirectory.child("mods/");
schematicDirectory = dataDirectory.child("schematics/");
bebuildDirectory = dataDirectory.child("be_builds/");
modDirectory.mkdirs();

View File

@@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
//stop looping when interrupted externally
return;
}
}catch(Exception e){
}catch(Throwable e){
e.printStackTrace();
}
}

View File

@@ -1240,7 +1240,7 @@ public class Blocks implements ContentList{
//region storage
coreShard = new CoreBlock("core-shard"){{
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000));
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
alwaysUnlocked = true;
health = 1100;
@@ -1249,7 +1249,7 @@ public class Blocks implements ContentList{
}};
coreFoundation = new CoreBlock("core-foundation"){{
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 400, Items.silicon, 3000));
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
health = 2000;
itemCapacity = 9000;
@@ -1257,7 +1257,7 @@ public class Blocks implements ContentList{
}};
coreNucleus = new CoreBlock("core-nucleus"){{
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000, Items.surgealloy, 3000));
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
health = 4000;
itemCapacity = 13000;

View File

@@ -3,8 +3,6 @@ package mindustry.content;
import mindustry.ctype.*;
import mindustry.game.*;
import java.io.*;
public class Loadouts implements ContentList{
public static Schematic
basicShard,
@@ -14,13 +12,9 @@ public class Loadouts implements ContentList{
@Override
public void load(){
try{
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
}catch(IOException e){
throw new RuntimeException(e);
}
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
}
}

View File

@@ -209,6 +209,7 @@ public class Logic implements ApplicationListener{
@Override
public void update(){
Events.fire(Trigger.update);
if(!state.is(State.menu)){
if(!net.client()){
@@ -260,7 +261,7 @@ public class Logic implements ApplicationListener{
}
}
if(!net.client() && !world.isInvalidMap() && !state.isEditor()){
if(!net.client() && !world.isInvalidMap() && !state.isEditor() && !state.rules.canGameOver){
checkGameOver();
}
}

View File

@@ -1,14 +1,14 @@
package mindustry.core;
import arc.*;
import mindustry.annotations.Annotations.*;
import arc.struct.*;
import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.CommandHandler.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
import mindustry.entities.*;
@@ -26,9 +26,11 @@ import mindustry.world.*;
import mindustry.world.blocks.storage.CoreBlock.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.zip.*;
import static arc.util.Log.*;
import static mindustry.Vars.*;
public class NetServer implements ApplicationListener{
@@ -217,7 +219,6 @@ public class NetServer implements ApplicationListener{
//playing in pvp mode automatically assigns players to teams
player.setTeam(assignTeam(player, playerGroup.all()));
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
sendWorldData(player);
@@ -599,6 +600,7 @@ public class NetServer implements ApplicationListener{
return false;
}
@Override
public void update(){
if(!headless && !closing && net.server() && state.is(State.menu)){
@@ -616,6 +618,20 @@ public class NetServer implements ApplicationListener{
}
}
/** Should only be used on the headless backend. */
public void openServer(){
try{
net.host(Config.port.num());
info("&lcOpened a server on port {0}.", Config.port.num());
}catch(BindException e){
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
state.set(State.menu);
}catch(IOException e){
err(e);
state.set(State.menu);
}
}
public void kickAll(KickReason reason){
for(NetConnection con : net.getConnections()){
con.kick(reason);

View File

@@ -125,7 +125,7 @@ public class Renderer implements ApplicationListener{
TileEntity core = player.getClosestCore();
if(core != null && player.spawner == null){
camera.position.lerpDelta(core.x, core.y, 0.08f);
}else{
}else if(core != null){
camera.position.lerpDelta(position, 0.08f);
}
}else if(control.input instanceof DesktopInput){

View File

@@ -9,9 +9,9 @@ import arc.util.io.*;
public class Version{
/** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */
public static String type;
public static String type = "unknown";
/** Build modifier, e.g. 'alpha' or 'release' */
public static String modifier;
public static String modifier = "unknown";
/** Number specifying the major version, e.g. '4' */
public static int number;
/** Build number, e.g. '43'. set to '-1' for custom builds. */

View File

@@ -217,7 +217,7 @@ public class World{
public void loadMap(Map map, Rules checkRules){
try{
SaveIO.load(map.file, new FilterContext(map));
}catch(Exception e){
}catch(Throwable e){
Log.err(e);
if(!headless){
ui.showErrorMessage("$map.invalid");

View File

@@ -7,11 +7,13 @@ import arc.graphics.*;
import arc.math.geom.*;
import mindustry.entities.traits.*;
import java.util.*;
import static mindustry.Vars.collisions;
/** Represents a group of a certain type of entity.*/
@SuppressWarnings("unchecked")
public class EntityGroup<T extends Entity>{
public class EntityGroup<T extends Entity> implements Iterable<T>{
private final boolean useTree;
private final int id;
private final Class<T> type;
@@ -253,8 +255,13 @@ public class EntityGroup<T extends Entity>{
return null;
}
/** Returns the logic-only array for iteration. */
/** Returns the array for iteration. */
public Array<T> all(){
return entityArray;
}
@Override
public Iterator<T> iterator(){
return entityArray.iterator();
}
}

View File

@@ -28,7 +28,9 @@ public class EventType{
exclusionDeath,
suicideBomb,
openWiki,
teamCoreDamage
teamCoreDamage,
socketConfigChanged,
update
}
public static class WinEvent{}

View File

@@ -70,6 +70,8 @@ public class Rules{
public boolean editor = false;
/** Whether the tutorial is enabled. False by default. */
public boolean tutorial = false;
/** Whether a gameover can happen at all. Set this to false to implement custom gameover conditions. */
public boolean canGameOver = true;
/** Starting items put in cores */
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
/** Blocks that cannot be placed. */

View File

@@ -249,6 +249,7 @@ public class Schematics implements Loadable{
public void placeLoadout(Schematic schem, int x, int y){
Stile coreTile = schem.tiles.find(s -> s.block instanceof CoreBlock);
if(coreTile == null) throw new IllegalArgumentException("Schematic has no core tile. Exiting.");
int ox = x - coreTile.x, oy = y - coreTile.y;
schem.tiles.each(st -> {
Tile tile = world.tile(st.x + ox, st.y + oy);
@@ -339,7 +340,8 @@ public class Schematics implements Loadable{
for(int cy = oy; cy <= oy2; cy++){
Tile tile = world.ltile(cx, cy);
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock)
&& (tile.entity.block.isVisible() || (tile.entity.block instanceof CoreBlock && Core.settings.getBool("coreselect")))){
int config = tile.entity.config();
if(tile.block().posConfig){
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);
@@ -368,8 +370,12 @@ public class Schematics implements Loadable{
//region IO methods
/** Loads a schematic from base64. May throw an exception. */
public static Schematic readBase64(String schematic) throws IOException{
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
public static Schematic readBase64(String schematic){
try{
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
}catch(IOException e){
throw new RuntimeException(e);
}
}
public static Schematic read(Fi file) throws IOException{

View File

@@ -82,6 +82,7 @@ public class MinimapRenderer implements Disposable{
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
for(Unit unit : units){
if(unit.isDead()) continue;
float rx = (unit.x - rect.x) / rect.width * w;
float ry = (unit.y - rect.y) / rect.width * h;

View File

@@ -10,6 +10,7 @@ import arc.scene.event.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.*;
import mindustry.core.GameState.*;
import mindustry.entities.traits.BuilderTrait.*;
@@ -135,6 +136,12 @@ public class DesktopInput extends InputHandler{
ui.listfrag.toggle();
}
if(player.getClosestCore() == null){
//move camera around
float camSpeed = 6f;
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta() * camSpeed));
}
if(Core.input.keyRelease(Binding.select)){
player.isShooting = false;
}

File diff suppressed because one or more lines are too long

View File

@@ -18,11 +18,6 @@ public class Administration{
private Array<ChatFilter> chatFilters = new Array<>();
public Administration(){
Core.settings.defaults(
"strict", true,
"servername", "Server"
);
load();
}
@@ -51,21 +46,12 @@ public class Administration{
Core.settings.putSave("playerlimit", limit);
}
public void setStrict(boolean on){
Core.settings.putSave("strict", on);
}
public boolean getStrict(){
return Core.settings.getBool("strict");
return Config.strict.bool();
}
public boolean allowsCustomClients(){
return Core.settings.getBool("allow-custom", !headless);
}
public void setCustomClients(boolean allowed){
Core.settings.put("allow-custom", allowed);
Core.settings.save();
return Config.allowCustomClients.bool();
}
/** Call when a player joins to update their information here. */
@@ -219,11 +205,7 @@ public class Administration{
}
public boolean isWhitelistEnabled(){
return Core.settings.getBool("whitelist", false);
}
public void setWhitelist(boolean enabled){
Core.settings.putSave("whitelist", enabled);
return Config.whitelist.bool();
}
public boolean isWhitelisted(String id, String usid){
@@ -333,6 +315,80 @@ public class Administration{
whitelist = Core.settings.getObject("whitelisted", Array.class, Array::new);
}
/** Server configuration definition. Each config value can be a string, boolean or number. */
public enum Config{
name("The server name as displayed on clients.", "Server", "servername"),
port("The port to host on.", Vars.port),
autoUpdate("Whether to auto-update and exit when a new bleeding-edge update arrives.", false),
startCommands("Commands run at startup. This should be a comma-separated list.", ""),
crashReport("Whether to send crash reports.", false, "crashreport"),
logging("Whether to log everything to files.", true),
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
socketInputPort("The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
socketInputAddress("The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),
allowCustomClients("Whether custom clients are allowed to connect.", !headless, "allow-custom"),
whitelist("Whether the whitelist is used.", false);
public static final Config[] all = values();
public final Object defaultValue;
public final String key, description;
final Runnable changed;
Config(String description, Object def){
this(description, def, null, null);
}
Config(String description, Object def, String key){
this(description, def, key, null);
}
Config(String description, Object def, Runnable changed){
this(description, def, null, changed);
}
Config(String description, Object def, String key, Runnable changed){
this.description = description;
this.key = key == null ? name() : key;
this.defaultValue = def;
this.changed = changed == null ? () -> {} : changed;
}
public boolean isNum(){
return defaultValue instanceof Integer;
}
public boolean isBool(){
return defaultValue instanceof Boolean;
}
public boolean isString(){
return defaultValue instanceof String;
}
public Object get(){
return Core.settings.get(key, defaultValue);
}
public boolean bool(){
return Core.settings.getBool(key, (Boolean)defaultValue);
}
public int num(){
return Core.settings.getInt(key, (Integer)defaultValue);
}
public String string(){
return Core.settings.getString(key, (String)defaultValue);
}
public void set(Object value){
Core.settings.putSave(key, value);
changed.run();
}
}
@Serialize
public static class PlayerInfo{
public String id;

View File

@@ -0,0 +1,173 @@
package mindustry.net;
import arc.*;
import arc.Net.*;
import arc.files.*;
import arc.func.*;
import arc.util.*;
import arc.util.async.*;
import arc.util.serialization.*;
import mindustry.core.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.net.Administration.*;
import mindustry.net.Packets.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
import java.io.*;
import java.net.*;
import static mindustry.Vars.*;
/** Handles control of bleeding edge builds. */
public class BeControl{
private static final int updateInterval = 60 * 2;
private AsyncExecutor executor = new AsyncExecutor(1);
private boolean checkUpdates = true;
private boolean updateAvailable;
private String updateUrl;
private int updateBuild;
/** @return whether this is a bleeding edge build. */
public boolean active(){
return Version.type.equals("bleeding-edge");
}
public BeControl(){
if(active()){
Timer.schedule(() -> {
if(checkUpdates && !mobile){
checkUpdate(t -> {});
}
}, 1, updateInterval);
}
}
/** asynchronously checks for updates. */
public void checkUpdate(Boolc done){
Core.net.httpGet("https://api.github.com/repos/Anuken/MindustryBuilds/releases/latest", res -> {
if(res.getStatus() == HttpStatus.OK){
Jval val = Jval.read(res.getResultAsString());
int newBuild = Strings.parseInt(val.getString("tag_name", "0"));
if(newBuild > Version.build){
Jval asset = val.get("assets").asArray().find(v -> v.getString("name", "").startsWith(headless ? "Mindustry-BE-Server" : "Mindustry-BE-Desktop"));
String url = asset.getString("browser_download_url", "");
updateAvailable = true;
updateBuild = newBuild;
updateUrl = url;
showUpdateDialog();
Core.app.post(() -> done.get(true));
}else{
Core.app.post(() -> done.get(false));
}
}else{
Core.app.post(() -> done.get(false));
}
}, error -> {
if(!headless){
ui.showException(error);
}else{
error.printStackTrace();
}
});
}
/** @return whether a new update is available */
public boolean isUpdateAvailable(){
return updateAvailable;
}
/** shows the dialog for updating the game on desktop, or a prompt for doing so on the server */
public void showUpdateDialog(){
if(!updateAvailable) return;
if(!headless){
checkUpdates = false;
ui.showCustomConfirm(Core.bundle.format("be.update", "") + " " + updateBuild, "$be.update.confirm", "$ok", "$be.ignore", () -> {
boolean[] cancel = {false};
float[] progress = {0};
int[] length = {0};
Fi file = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
FloatingDialog dialog = new FloatingDialog("$be.updating");
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
try{
Runtime.getRuntime().exec(new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()});
System.exit(0);
}catch(IOException e){
ui.showException(e);
}
}, e -> {
dialog.hide();
ui.showException(e);
});
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
dialog.buttons.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
cancel[0] = true;
dialog.hide();
}).size(210f, 64f);
dialog.setFillParent(false);
dialog.show();
}, () -> checkUpdates = false);
}else{
Log.info("&lcA new update is available: &lyBleeding Edge build {0}", updateBuild);
if(Config.autoUpdate.bool()){
Log.info("&lcAuto-downloading next version...");
try{
//download new file from github
Fi source = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
Fi dest = source.sibling("server-be-" + updateBuild + ".jar");
download(updateUrl, dest,
len -> Core.app.post(() -> Log.info("&ly| Size: {0} MB.", Strings.fixed((float)len / 1024 / 1024, 2))),
progress -> {},
() -> false,
() -> Core.app.post(() -> {
netServer.kickAll(KickReason.serverRestarting);
Threads.sleep(32);
Log.info("&lcVersion downloaded, exiting. Note that if you are not using a auto-restart script, the server will not restart automatically.");
//replace old file with new
dest.copyTo(source);
dest.delete();
System.exit(2); //this will cause a restart if using the script
}),
Throwable::printStackTrace);
}catch(Exception e){
e.printStackTrace();
}
}
checkUpdates = false;
}
}
private void download(String furl, Fi dest, Intc length, Floatc progressor, Boolp canceled, Runnable done, Cons<Throwable> error){
executor.submit(() -> {
try{
HttpURLConnection con = (HttpURLConnection)new URL(furl).openConnection();
BufferedInputStream in = new BufferedInputStream(con.getInputStream());
OutputStream out = dest.write(false, 4096);
byte[] data = new byte[4096];
long size = con.getContentLength();
long counter = 0;
length.get((int)size);
int x;
while((x = in.read(data, 0, data.length)) >= 0 && !canceled.get()){
counter += x;
progressor.get((float)counter / (float)size);
out.write(data, 0, x);
}
out.close();
in.close();
if(!canceled.get()) done.run();
}catch(Throwable e){
error.get(e);
}
});
}
}

View File

@@ -27,7 +27,9 @@ public class CrashSender{
exception.printStackTrace();
//don't create crash logs for custom builds, as it's expected
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return;
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))){
ret();
}
//attempt to load version regardless
if(Version.number == 0){
@@ -63,7 +65,7 @@ public class CrashSender{
try{
//check crash report setting
if(!Core.settings.getBool("crashreport", true)){
return;
ret();
}
}catch(Throwable ignored){
//if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway
@@ -72,14 +74,14 @@ public class CrashSender{
try{
//check any mods - if there are any, don't send reports
if(Vars.mods != null && !Vars.mods.list().isEmpty()){
return;
ret();
}
}catch(Throwable ignored){
}
//do not send exceptions that occur for versions that can't be parsed
if(Version.number == 0){
return;
ret();
}
boolean netActive = false, netServer = false;
@@ -130,12 +132,16 @@ public class CrashSender{
while(!sent[0]){
Thread.sleep(30);
}
}catch(InterruptedException ignored){
}
}catch(InterruptedException ignored){}
}catch(Throwable death){
death.printStackTrace();
System.exit(1);
}
ret();
}
private static void ret(){
System.exit(1);
}
private static void httpPost(String url, String content, Cons<HttpResponse> success, Cons<Throwable> failure){

View File

@@ -1,5 +1,6 @@
package mindustry.net;
import mindustry.*;
import mindustry.game.*;
public class Host{
@@ -11,7 +12,7 @@ public class Host{
public final int version;
public final String versionType;
public final Gamemode mode;
public int ping;
public int ping, port = Vars.port;
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){
this.name = name;

View File

@@ -1,12 +1,12 @@
package mindustry.net;
import arc.*;
import arc.util.*;
import mindustry.core.*;
import mindustry.entities.type.*;
import mindustry.game.*;
import mindustry.io.*;
import mindustry.maps.Map;
import mindustry.net.Administration.*;
import java.io.*;
import java.nio.*;
@@ -62,7 +62,7 @@ public class NetworkIO{
}
public static ByteBuffer writeServerData(){
String name = (headless ? Core.settings.getString("servername") : player.name);
String name = (headless ? Config.name.string() : player.name);
String map = world.getMap() == null ? "None" : world.getMap().name();
ByteBuffer buffer = ByteBuffer.allocate(256);

View File

@@ -15,7 +15,8 @@ public class Packets{
public enum KickReason{
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit;
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch,
whitelist, playerLimit, serverRestarting;
public final boolean quiet;

View File

@@ -288,7 +288,12 @@ public class JoinDialog extends FloatingDialog{
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
for(String host : defaultServers){
net.pingHost(host, port, this::addLocalHost, e -> {});
String resaddress = host.contains(":") ? host.split(":")[0] : host;
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
net.pingHost(resaddress, resport, res -> {
res.port = resport;
addLocalHost(res);
}, e -> {});
}
}
@@ -314,7 +319,7 @@ public class JoinDialog extends FloatingDialog{
local.row();
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, port, host.version))
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
.width(w).pad(5f).get();
button.clearChildren();
buildServer(host, button);
@@ -362,7 +367,7 @@ public class JoinDialog extends FloatingDialog{
servers = Core.settings.getObject("server-list", Array.class, Array::new);
//get servers
Core.net.httpGet(serverJsonURL, result -> {
Core.net.httpGet(becontrol.active() ? serverJsonBeURL : serverJsonURL, result -> {
try{
Jval val = Jval.read(result.getResultAsString());
Core.app.post(() -> {

View File

@@ -32,7 +32,7 @@ public class ModsDialog extends FloatingDialog{
buttons.row();
buttons.addImageTextButton("$mods.guide", Icon.wiki,
buttons.addImageTextButton("$mods.guide", Icon.link,
() -> Core.net.openURI(modGuideURL))
.size(210, 64f);

View File

@@ -163,7 +163,7 @@ public class SchematicsDialog extends FloatingDialog{
setup();
ui.showInfoFade("$schematic.saved");
showInfo(s);
}catch(Exception e){
}catch(Throwable e){
ui.showException(e);
}
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));

View File

@@ -229,6 +229,7 @@ public class SettingsMenuDialog extends SettingsDialog{
game.checkPref("savecreate", true);
game.checkPref("blockreplace", true);
game.checkPref("conveyorpathfinding", true);
game.checkPref("coreselect", false);
game.checkPref("hints", true);
if(!mobile){
game.checkPref("buildautopause", false);

View File

@@ -59,6 +59,18 @@ public class MenuFragment extends Fragment{
if(mobile){
parent.fill(c -> c.bottom().left().addButton("", Styles.infot, ui.about::show).size(84, 45));
parent.fill(c -> c.bottom().right().addButton("", Styles.discordt, ui.discord::show).size(84, 45));
}else if(becontrol.active()){
parent.fill(c -> c.bottom().right().addImageTextButton("$be.check", Icon.refreshSmall, () -> {
ui.loadfrag.show();
becontrol.checkUpdate(result -> {
ui.loadfrag.hide();
if(!result){
ui.showInfo("$be.noupdates");
}
});
}).size(200, 60).update(t -> {
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
}));
}
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));

View File

@@ -257,7 +257,7 @@ public class Tile implements Position, TargetTrait{
}
public boolean solid(){
return block.solid || block.isSolidFor(this) || (isLinked() && link().solid());
return block.solid || block.isSolidFor(this) || (isLinked() && link() != this && link().solid());
}
public boolean breakable(){

View File

@@ -5,6 +5,7 @@ import arc.func.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.entities.*;
@@ -37,7 +38,7 @@ public class ForceProjector extends Block{
private static ForceProjector paramBlock;
private static ForceEntity paramEntity;
private static Cons<AbsorbTrait> shieldConsumer = trait -> {
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && Intersector.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
trait.absorb();
Effects.effect(Fx.absorb, trait);
paramEntity.hit = 1f;
@@ -111,17 +112,6 @@ public class ForceProjector extends Block{
entity.warmup = Mathf.lerpDelta(entity.warmup, entity.efficiency(), 0.1f);
/*
if(entity.power.status < relativePowerDraw){
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f);
entity.power.status = 0f;
if(entity.warmup <= 0.09f){
entity.broken = true;
}
}else{
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f);
}*/
if(entity.buildup > 0){
float scale = !entity.broken ? cooldownNormal : cooldownBrokenBase;
ConsumeLiquidFilter cons = consumes.get(ConsumeType.liquid);
@@ -159,13 +149,6 @@ public class ForceProjector extends Block{
return (radius + entity.phaseHeat * phaseRadiusBoost) * entity.radscl;
}
boolean isInsideHexagon(float x0, float y0, float d, float x, float y){
float dx = Math.abs(x - x0) / d;
float dy = Math.abs(y - y0) / d;
float a = 0.25f * Mathf.sqrt3;
return (dy <= a) && (a * dx + 0.25 * dy <= 0.5 * a);
}
@Override
public void draw(Tile tile){
super.draw(tile);

View File

@@ -4,6 +4,7 @@ import arc.Core;
import arc.graphics.g2d.Draw;
import arc.graphics.g2d.TextureRegion;
import arc.math.Mathf;
import arc.util.*;
import mindustry.content.Fx;
import mindustry.content.Liquids;
import mindustry.entities.Effects;
@@ -51,8 +52,8 @@ public class SolidPump extends Pump{
public void setBars(){
super.setBars();
bars.add("efficiency", entity -> new Bar(() ->
Core.bundle.formatFloat("bar.efficiency",
((((SolidPumpEntity)entity).boost + 1f) * ((SolidPumpEntity)entity).warmup) * 100 * percentSolid(entity.tile.x, entity.tile.y), 1),
Core.bundle.formatFloat("bar.pumpspeed",
((SolidPumpEntity)entity).lastPump / Time.delta() * 60, 1),
() -> Pal.ammo,
() -> ((SolidPumpEntity)entity).warmup));
}
@@ -104,11 +105,13 @@ public class SolidPump extends Pump{
if(tile.entity.cons.valid() && typeLiquid(tile) < liquidCapacity - 0.001f){
float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction * entity.efficiency());
tile.entity.liquids.add(result, maxPump);
entity.lastPump = maxPump;
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
if(Mathf.chance(entity.delta() * updateEffectChance))
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
}else{
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f);
entity.lastPump = 0f;
}
entity.pumpTime += entity.warmup * entity.delta();
@@ -153,5 +156,6 @@ public class SolidPump extends Pump{
public float warmup;
public float pumpTime;
public float boost;
public float lastPump;
}
}