Compare commits

..

35 Commits

Author SHA1 Message Date
Anuken
d61bb66418 Fixed capture wave being set to 0 in some situations 2020-12-05 18:03:21 -05:00
Anuken
b0f62f25e6 No suggested mods 2020-12-05 17:53:49 -05:00
Anuken
bdbc9b865b Fixed distort artifacts 2020-12-05 17:50:29 -05:00
Anuken
f6428b8857 Fixed #3808 2020-12-05 16:40:54 -05:00
Anuken
cb3b43cc48 Disabled cursor scaling 2020-12-05 14:23:29 -05:00
Anuken
587c63fc92 Merge remote-tracking branch 'origin/master' 2020-12-05 14:16:06 -05:00
Anuken
97fa8057ba Fixed #3805 2020-12-05 14:16:02 -05:00
Anuken
6a2bb7b01f Merge pull request #3794 from sharlotte-mobile/patch-1
ko hotfix
2020-12-05 11:05:55 -05:00
Anuken
edbced6642 Fixed #3798 2020-12-05 10:58:30 -05:00
Anuken
8c0477f6bd Fixed #3795 2020-12-05 10:30:46 -05:00
Sharlotte
42f0364319 ko hotfix
translate all missing values
removed over-translated values
and do Uniform some terms, correction of typos.
2020-12-05 20:10:03 +09:00
Anuken
6570d23512 Fixed #3786 2020-12-04 19:24:09 -05:00
Anuken
7ec3ab5a17 Fixed #3784 2020-12-04 18:55:37 -05:00
Anuken
6af015cc05 Round maxProduction up 2020-12-04 15:22:59 -05:00
Anuken
7938e02e78 Fixed maxProduction stat being perSecond 2020-12-04 15:21:43 -05:00
Anuken
bac4bb8c8f Heighten export cap when production is negative 2020-12-04 15:20:15 -05:00
Anuken
dec5a2aaaf Minor campaign tweaks 2020-12-04 15:07:35 -05:00
Anuken
7b1c60c24f Testing tests 2020-12-04 12:23:02 -05:00
Anuken
8b8d990852 Testing tests 2020-12-04 12:16:47 -05:00
Anuken
6583cc0b5d Testing tests 2020-12-04 11:38:43 -05:00
Anuken
5c3ae425ae Display locked sectors / Core incinerate hint 2020-12-04 11:31:27 -05:00
Anuken
744b1b2037 Looking into test failure 2020-12-04 10:56:47 -05:00
Anuken
a313ca8a26 Checking if tests fail 2020-12-04 10:44:39 -05:00
Anuken
87204df3ee Merge remote-tracking branch 'origin/master' 2020-12-04 10:25:51 -05:00
Anuken
bc70c08820 Fixed #3756 2020-12-04 10:25:41 -05:00
Anuken
2c2828617f Create ISSUES.md 2020-12-04 10:03:54 -05:00
Anuken
a451ad895c Slight sei buff 2020-12-03 20:29:58 -05:00
Anuken
d13b69c36f Sort community servers by ping 2020-12-03 16:49:30 -05:00
Anuken
d64f603e08 Merge branch 'master' of https://github.com/Anuken/Mindustry 2020-12-03 12:08:35 -05:00
Anuken
3d2c094056 arc 2020-12-03 12:08:31 -05:00
Anuken
64e99f613e Update bug_report.md 2020-12-03 11:08:00 -05:00
Anuken
51b4078288 Clean tests before running 2020-12-03 11:05:02 -05:00
Anuken
db8d099fcc arc 2020-12-03 10:29:19 -05:00
Anuken
002be46915 Updated miniaudio version 2020-12-03 10:28:57 -05:00
Anuken
9fa2691b02 Fixed campaign idle boss spoofing 2020-12-03 10:05:52 -05:00
47 changed files with 449 additions and 225 deletions

View File

@@ -21,7 +21,7 @@ assignees: ''
If you remove the line above without reading it properly and understanding what it means, I will reap your soul. Even if you're playing on someone's server, you can still save the game to a slot. If you remove the line above without reading it properly and understanding what it means, I will reap your soul. Even if you're playing on someone's server, you can still save the game to a slot.
**Crash report**: *The contents of relevant crash report files. REQUIRED if you are reporting a crash.* **(Crash) logs**: *Either crash reports from the crash folder, or the file you get when you go into Settings -> Game Data -> Export Crash logs. REQUIRED if you are reporting a crash.*
--- ---

View File

@@ -13,7 +13,7 @@ jobs:
with: with:
java-version: 14 java-version: 14
- name: Run unit tests - name: Run unit tests
run: ./gradlew test run: ./gradlew clean cleanTest test
- name: Trigger BE build - name: Trigger BE build
if: ${{ github.repository == 'Anuken/Mindustry' }} if: ${{ github.repository == 'Anuken/Mindustry' }}
run: | run: |

52
ISSUES.md Normal file
View File

@@ -0,0 +1,52 @@
# Why was my issue closed?
This document goes over some common causes for issue closures.
## You did not fill in the template
I can't debug the problem unless you provide the information the template asks for.
If you cannot put in the effort to fill out a template, then don't expect me to put in the effort to fix it.
## Your issue was already reported
If the problem in your issue has already been encountered before, it will be closed - especially if your report doesn't provide any new information.
Make sure you search the *closed* issues before making an issue.
I do not link the specific issue(s) that report the same problem, because searching takes time - if you're interested in finding them, you should be able to do so without my help.
To be clear: I do **not** expect users to look at *all* previous issues, or do a comprehensive stack trace analysis to see if their crash was already reported.
## Your issue was already fixed
The problem you reported has been addressed. Note that this does **not** mean that the latest stable version of Mindustry has the fix!
It simply means that I have committed (or am about to commit) a patch that fixes it *on the current development branch*.
## Your issue is missing a crash report or log
If the game crashes without a specific cause, and you don't send me a log, I can't fix it. There is no way for me to know what went wrong.
During a normal crash, the game should tell you where the log is saved. If not, you should still be able to look in the game's crash folder on most operating systems, or export the logs in *Settings -> Game Data -> Export Crash Logs*.
## Your issue is missing saves or screenshots
Even if you think your problem happens everywhere and saves/screenshots are redundant, this is frequently not the case.
If I cannot reproduce the problem on my own saves and you have not linked any of your own, then the problem is likely to be save-specific. If you do not send me any, the problem cannot be investigated further.
## Your issue is related to an external program
If Mindustry causes something else to crash or misbehave, I am very unlikely to fix it. Unless the problem is serious, widespread and/or clearly a bug *in Mindustry*, it is not my responsibility.
Similarly, if you use another (invasive) program to change how Mindustry works, and something goes wrong, that is not my problem. Don't do it.
## Your issue is caused by mods
Crashes and bugs related to installed mods should be reported in the relevant mod repository, not here.
*Note that problems with the Mindustry modding API are a separate problem, and do not apply.*
## I cannot reproduce your issue
If I follow your instructions and am repeatedly unable to reproduce the problem you've reported, then it is very unlikely to be fixed.
Either the problem is device-specific, or there is not enough information given for me to be able to reproduce it.
I may attempt to change some code if I think it will make the issue less likely to occur, but without knowing for sure, the issue cannot be considered truly "fixed".
As I cannot make any further progress on the problem, there is no reason to keep it open. If it is a common bug/crash, other people will come along with information that may shed some light on the issue.

View File

@@ -69,7 +69,7 @@ stat.delivered = Resources Launched:
stat.playtime = Time Played:[accent] {0} stat.playtime = Time Played:[accent] {0}
stat.rank = Final Rank: [accent]{0} stat.rank = Final Rank: [accent]{0}
globalitems = [accent]Global Items globalitems = [accent]Total Items
map.delete = Are you sure you want to delete the map "[accent]{0}[]"? map.delete = Are you sure you want to delete the map "[accent]{0}[]"?
level.highscore = High Score: [accent]{0} level.highscore = High Score: [accent]{0}
level.select = Level Select level.select = Level Select
@@ -1282,6 +1282,7 @@ hint.generator = \uf879 [accent]Combustion Generators[] burn coal and transmit p
hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down. hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down.
hint.coreUpgrade = Cores can be upgraded by [accent]placing higher-tier cores over them[].\n\nPlace a  [accent]Foundation[] core over the  [accent]Shard[] core. Make sure it is free from nearby obstructions. hint.coreUpgrade = Cores can be upgraded by [accent]placing higher-tier cores over them[].\n\nPlace a  [accent]Foundation[] core over the  [accent]Shard[] core. Make sure it is free from nearby obstructions.
hint.presetLaunch = Gray [accent]landing zone sectors[], such as [accent]Frozen Forest[], can be launched to from anywhere. They do not require capture of nearby territory.\n\n[accent]Numbered sectors[], such as this one, are [accent]optional[]. hint.presetLaunch = Gray [accent]landing zone sectors[], such as [accent]Frozen Forest[], can be launched to from anywhere. They do not require capture of nearby territory.\n\n[accent]Numbered sectors[], such as this one, are [accent]optional[].
hint.coreIncinerate = After the core is filled to capacity with an item, any extra items of that type it receives will be [accent]incinerated[].
item.copper.description = Used in all types of construction and ammunition. item.copper.description = Used in all types of construction and ammunition.
item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced. item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced.

View File

@@ -1,5 +1,5 @@
credits.text = 만든이: [royal]Anuken[] - [sky]anukendev@gmail.com[] credits.text = 만든이: [royal]Anuken[] - [sky]anukendev@gmail.com[]
credits = 제작 credits = 제작
contributors = 번역가와 기여자 contributors = 번역가와 기여자
discord = Mindustry Discord 서버에 가입하세요! discord = Mindustry Discord 서버에 가입하세요!
link.discord.description = Mindustry Discord 공식 대화방 link.discord.description = Mindustry Discord 공식 대화방
@@ -15,7 +15,7 @@ link.wiki.description = 공식 Mindustry 위키
link.suggestions.description = 새 기능 제안하기 link.suggestions.description = 새 기능 제안하기
linkfail = 링크를 열지 못했습니다!\nURL이 클립보드에 복사되었습니다. linkfail = 링크를 열지 못했습니다!\nURL이 클립보드에 복사되었습니다.
screenshot = 스크린 캡처가 {0} 에 저장되었습니다. screenshot = 스크린 캡처가 {0} 에 저장되었습니다.
screenshot.invalid = 맵이 너무 커서 스크린 캡처에 사용될 메모리가 부족할 수 있습니다. screenshot.invalid = 맵이 너무 커서 스크린 캡처에 사용될 메모리가 부족니다.
gameover = 게임 오버 gameover = 게임 오버
gameover.disconnect = 연결 끊김 gameover.disconnect = 연결 끊김
gameover.pvp = [accent]{0}[] 팀이 승리했습니다! gameover.pvp = [accent]{0}[] 팀이 승리했습니다!
@@ -75,7 +75,7 @@ level.highscore = 최고 점수: [accent]{0}
level.select = 맵 선택 level.select = 맵 선택
level.mode = 게임 모드: level.mode = 게임 모드:
coreattack = < 코어가 공격을 받고 있습니다! > coreattack = < 코어가 공격을 받고 있습니다! >
nearpoint = [[ [scarlet]즉시 적 소환구역에서 떠나세요[] ]\n인근 건물들과 유닛들은 초토화됩니다! nearpoint = [[ [scarlet]즉시 적 소환구역에서 떠나십시오[] ]\n단계가 시작하는 순간 인근 건물들과 유닛들이 전멸됩니다!
database = 코어 데이터베이스 database = 코어 데이터베이스
savegame = 게임 저장 savegame = 게임 저장
loadgame = 게임 불러오기 loadgame = 게임 불러오기
@@ -149,7 +149,7 @@ unlocked = 새로운 콘텐츠가 해금되었습니다!
available = 새로운 콘텐츠 해금이 가능합니다! available = 새로운 콘텐츠 해금이 가능합니다!
completed = [accent]완료됨 completed = [accent]완료됨
techtree = 연구 기록 techtree = 연구 기록
research.legacy = [accent]5.0[] 연구 데이터를 찾았습니다.\n[accent]이 데이터를 불러오시겠습니까?[], 아니면 이 데이터를 무시하고 캠페인을 새로 시작하시겠습니까? [accent]무시한 데이터는 삭제됩니다.[] (권장됨)? research.legacy = [accent]5.0[] 연구 데이터를 찾았습니다.\n[accent]이 데이터를 불러오시겠습니까?[], 아니면 이 데이터를 무시하고 캠페인을 새로 시작하시겠습니까? [accent]무시한 데이터는 삭제됩니다.[] (권장됨)
research.load = 불러오기 research.load = 불러오기
research.discard = 무시하기 research.discard = 무시하기
research.list = [lightgray]연구: research.list = [lightgray]연구:
@@ -167,7 +167,7 @@ server.kicked.serverClose = 서버 닫힘.
server.kicked.vote = 당신은 투표로 추방되었습니다. 안녕히 가세요! server.kicked.vote = 당신은 투표로 추방되었습니다. 안녕히 가세요!
server.kicked.clientOutdated = 구버전 클라이언트입니다! 게임을 업데이트하세요! server.kicked.clientOutdated = 구버전 클라이언트입니다! 게임을 업데이트하세요!
server.kicked.serverOutdated = 구버전 서버입니다! 호스트에게 업데이트를 요청하세요! server.kicked.serverOutdated = 구버전 서버입니다! 호스트에게 업데이트를 요청하세요!
server.kicked.banned = 당신은 이 서버에서 차단되었습니다. server.kicked.banned = 당신은 이 서버에서 영구적으로 차단되었습니다.
server.kicked.typeMismatch = 이 서버는 현재 빌드 유형과 호환되지 않습니다. server.kicked.typeMismatch = 이 서버는 현재 빌드 유형과 호환되지 않습니다.
server.kicked.playerLimit = 서버의 인원이 꽉 찼습니다. 빈 슬롯이 생길 때까지 기다려주세요. server.kicked.playerLimit = 서버의 인원이 꽉 찼습니다. 빈 슬롯이 생길 때까지 기다려주세요.
server.kicked.recentKick = 최근에 추방되었습니다.\n추방 쿨타임이 끝날 때까지 기다리세요. server.kicked.recentKick = 최근에 추방되었습니다.\n추방 쿨타임이 끝날 때까지 기다리세요.
@@ -196,6 +196,7 @@ servers.local = 로컬 서버
servers.remote = 원격 서버 servers.remote = 원격 서버
servers.global = 커뮤니티 서버 servers.global = 커뮤니티 서버
servers.disclaimer = 커뮤니티 서버는 개발자가 소유하거나 제어하지 [accent]않습니다[].\n\n서버들은 전연령대에 적합하지 않은 사용자 지정 콘텐츠를 보유할 수도 있습니다.
servers.showhidden = 서버 숨기기 / 보이기 servers.showhidden = 서버 숨기기 / 보이기
server.shown = 서버 숨기기 server.shown = 서버 숨기기
server.hidden = 서버 보이기 server.hidden = 서버 보이기
@@ -222,8 +223,8 @@ confirmban = 정말로 "{0}[white]" 을(를) 차단하시겠습니까?
confirmkick = 정말로 "{0}[white]" 을(를) 추방하시겠습니까? confirmkick = 정말로 "{0}[white]" 을(를) 추방하시겠습니까?
confirmvotekick = 정말로 "{0}[white]" 을(를) 투표로 추방하시겠습니까? confirmvotekick = 정말로 "{0}[white]" 을(를) 투표로 추방하시겠습니까?
confirmunban = 정말로 이 플레이어를 차단 해제하시겠습니까? confirmunban = 정말로 이 플레이어를 차단 해제하시겠습니까?
confirmadmin = 정말로 "{0}[white]" 을(를) 관리자로 만들겠습니까? confirmadmin = 정말로 "{0}[white]" 을(를) 관리자로 임명하겠습니까?
confirmunadmin = 정말로 "{0}[white]"의 관리자 상태를 제거하시겠습니까? confirmunadmin = 정말로 "{0}[white]"의 관리자를 박탈하시겠습니까?
joingame.title = 게임 참가 joingame.title = 게임 참가
joingame.ip = 주소: joingame.ip = 주소:
disconnect = 연결이 끊어졌습니다. disconnect = 연결이 끊어졌습니다.
@@ -293,11 +294,11 @@ quit.confirm.tutorial = 튜토리얼을 종료하시겠습니까?\n튜토리얼
loading = [accent]불러오는중... loading = [accent]불러오는중...
reloading = [accent]모드 새로고침하는중... reloading = [accent]모드 새로고침하는중...
saving = [accent]저장중... saving = [accent]저장중...
respawn = [accent][[{0}][] 키를 눌러 코어에서 부활 respawn = [accent][[{0}][] 키를 눌러 코어에서 부활하세요.
cancelbuilding = [accent][[{0}][] 를 눌러 계획 초기화 cancelbuilding = [accent][[{0}][] 를 눌러 건설 계획 초기화하세요.
selectschematic = [accent][[{0}][] 를 눌러 선택+복사 selectschematic = [accent][[{0}][] 를 눌러 선택+복사하세요.
pausebuilding = [accent][[{0}][] 를 눌러 건설 일시중지 pausebuilding = [accent][[{0}][] 를 눌러 건설 일시중지하세요.
resumebuilding = [scarlet][[{0}][] 를 눌러 건설 재개 resumebuilding = [scarlet][[{0}][] 를 눌러 건설 재개하세요.
showui = UI를 .\n[accent][[{0}][] 키를 눌러 UI를 활성화하세요. showui = UI를 .\n[accent][[{0}][] 키를 눌러 UI를 활성화하세요.
wave = [accent]{0} 단계 wave = [accent]{0} 단계
wave.cap = [accent]단계 {0}/{1} wave.cap = [accent]단계 {0}/{1}
@@ -386,7 +387,7 @@ editor.errorimage = 이것은 맵이 아니라 사진입니다.\n\n3.5/build 40
editor.errorlegacy = 이 맵은 너무 오래되어 더 이상 지원되지 않는 구형 맵 형식을 사용합니다. editor.errorlegacy = 이 맵은 너무 오래되어 더 이상 지원되지 않는 구형 맵 형식을 사용합니다.
editor.errornot = 맵 파일이 아닙니다. editor.errornot = 맵 파일이 아닙니다.
editor.errorheader = 이 맵 파일은 유효하지 않거나 손상되었습니다. editor.errorheader = 이 맵 파일은 유효하지 않거나 손상되었습니다.
editor.errorname = 맵에 이름이 지정되어 있지 않습니다. 저장 파일을 불러오려 합니까? editor.errorname = 맵에 이름이 지정되어 있지 않습니다. 저장 파일을 불러오려고 시도하는 건가요?
editor.update = 업데이트 editor.update = 업데이트
editor.randomize = 무작위 editor.randomize = 무작위
editor.apply = 적용 editor.apply = 적용
@@ -427,7 +428,7 @@ toolmode.replaceall.description = 맵에 있는 모든 블록을 재배치합니
toolmode.orthogonal = 직각 toolmode.orthogonal = 직각
toolmode.orthogonal.description = 직각으로 블록을 배치합니다. toolmode.orthogonal.description = 직각으로 블록을 배치합니다.
toolmode.square = 정사각형 toolmode.square = 정사각형
toolmode.square.description = 정사각형 형태의 브러시. toolmode.square.description = 정사각형 형태의 브러시로 교체합니다.
toolmode.eraseores = 자원 초기화 toolmode.eraseores = 자원 초기화
toolmode.eraseores.description = 자원만 초기화합니다. toolmode.eraseores.description = 자원만 초기화합니다.
toolmode.fillteams = 팀 채우기 toolmode.fillteams = 팀 채우기
@@ -486,7 +487,7 @@ memory2 = Mem:\n {0}mb +\n {1}mb
language.restart = 언어 설정을 적용하려면 게임을 다시 시작하세요. language.restart = 언어 설정을 적용하려면 게임을 다시 시작하세요.
settings = 설정 settings = 설정
tutorial = 튜토리얼 tutorial = 튜토리얼
tutorial.retake = 튜토리얼 다시 시작 tutorial.retake = 튜토리얼 다시 시작하기
editor = 편집기 editor = 편집기
mapeditor = 맵 편집기 mapeditor = 맵 편집기
@@ -500,8 +501,8 @@ requirement.research = {0} 연구
requirement.capture = {0} 점령 requirement.capture = {0} 점령
bestwave = [lightgray]최고 단계: {0} bestwave = [lightgray]최고 단계: {0}
launch.text = 출격 launch.text = 출격
research.multiplayer = 캠페인 멀티 플레이 시에는 해당 캠페인 서버의 주최자만 연구할 수 있습니다. research.multiplayer = 캠페인 멀티 플레이 시에는 해당 캠페인 서버의 호스트만 연구할 수 있습니다.
map.multiplayer = 캠페인 멀티 플레이 시에는 해당 캠페인 서버의 주최자만 다른 섹터들을 보고, 이동이 가능합니다. map.multiplayer = 캠페인 멀티 플레이 시에는 해당 캠페인 서버의 호스트만 다른 섹터들을 보고, 이동이 가능합니다.
uncover = 지역 개방 uncover = 지역 개방
configure = 초기자원 설정 configure = 초기자원 설정
@@ -518,7 +519,7 @@ boss.health = 수호자 체력
connectfail = [scarlet]연결 오류:\n\n[accent]{0} connectfail = [scarlet]연결 오류:\n\n[accent]{0}
error.unreachable = 서버에 연결하지 못했습니다.\n서버 주소가 정확히 입력되었나요? error.unreachable = 서버에 연결하지 못했습니다.\n서버 주소가 정확히 입력되었나요?
error.invalidaddress = 잘못된 주소입니다. error.invalidaddress = 잘못된 주소입니다.
error.timedout = 시간 초과!\n서버에 포트 포워딩이 설정되어 있고 주소가 올바른지 확인하십시오. error.timedout = 시간 초과!\n서버에 포트 포워딩이 설정되어 있고 주소가 올바른지 확인하세요.
error.mismatch = 패킷 오류\n클라이언트/서버 버전이 일치하지 않습니다.\n접속하려는 서버가 최신 버전인지 확인하세요! error.mismatch = 패킷 오류\n클라이언트/서버 버전이 일치하지 않습니다.\n접속하려는 서버가 최신 버전인지 확인하세요!
error.alreadyconnected = 이미 접속 중입니다. error.alreadyconnected = 이미 접속 중입니다.
error.mapnotfound = 맵 파일을 찾을 수 없습니다! error.mapnotfound = 맵 파일을 찾을 수 없습니다!
@@ -535,7 +536,7 @@ weather.fog.name = 안개
sectors.unexplored = [lightgray]미개척지 sectors.unexplored = [lightgray]미개척지
sectors.resources = 자원: sectors.resources = 자원:
sectors.production = 분당 자원 생산량: sectors.production = 분당 자원 생산량:
sectors.export = Export: sectors.export = 분당 자원 수출량:
sectors.time = 지역 진행 시간: sectors.time = 지역 진행 시간:
sectors.threat = 지역 위험도: sectors.threat = 지역 위험도:
sectors.wave = 진행 중 단계: sectors.wave = 진행 중 단계:
@@ -546,7 +547,7 @@ sectors.select = 선택
sectors.nonelaunch = [lightgray]없음 (sun) sectors.nonelaunch = [lightgray]없음 (sun)
sectors.rename = 구역 이름 변경 sectors.rename = 구역 이름 변경
sectors.enemybase = [scarlet]적 기지 sectors.enemybase = [scarlet]적 기지
sectors.vulnerable = [scarlet]]취약 sectors.vulnerable = [scarlet]취약
sectors.underattack = [scarlet]공격받고 있습니다! [accent]{0}% 손상됨. sectors.underattack = [scarlet]공격받고 있습니다! [accent]{0}% 손상됨.
sectors.survives = [accent]{0} 단계 이상 버티세요. sectors.survives = [accent]{0} 단계 이상 버티세요.
sectors.go = 지역 진입 sectors.go = 지역 진입
@@ -566,14 +567,14 @@ threat.eradication = 극한
planets = 태양계 planets = 태양계
planet.serpulo.name = 세르 planet.serpulo.name = 세르
planet.sun.name = 태양 planet.sun.name = 태양
sector.impact0078.name = 폐허 : Impact 0078 sector.impact0078.name = 폐허 : Impact 0078
sector.groundZero.name = Zero 전초기지 sector.groundZero.name = Zero 전초기지
sector.craters.name = 크레이터 sector.craters.name = 크레이터
sector.frozenForest.name = 얼어붙은 숲 sector.frozenForest.name = 얼어붙은 숲
sector.ruinousShores.name = 폐허 : 해안가 sector.ruinousShores.name = 파괴된 해안가
sector.stainedMountains.name = 얼룩진 산맥 sector.stainedMountains.name = 얼룩진 산맥
sector.desolateRift.name = 황폐한 협곡 sector.desolateRift.name = 황폐한 협곡
sector.nuclearComplex.name = 핵 생산 단지 sector.nuclearComplex.name = 핵 생산 단지
@@ -606,15 +607,15 @@ sector.planetaryTerminal.description = 이 행성에서의 마지막 전투를
settings.language = 언어 settings.language = 언어
settings.data = 게임 데이터 settings.data = 게임 데이터
settings.reset = 설정 초기화 settings.reset = 설정 초기화
settings.rebind = 키 설정 settings.rebind = 조작키 설정
settings.resetKey = 초기화 settings.resetKey = 조작키 설정 초기화
settings.controls = 조작 settings.controls = 조작
settings.game = 게임 settings.game = 게임
settings.sound = 소리 settings.sound = 소리
settings.graphics = 그래픽 settings.graphics = 그래픽
settings.cleardata = 게임 데이터 초기화 settings.cleardata = 게임 데이터 초기화
settings.clear.confirm = 정말로 이 데이터를 지우시겠습니까?\n되돌릴 수 없습니다! settings.clear.confirm = 정말로 이 데이터를 지우시겠습니까?\n되돌릴 수 없습니다!
settings.clearall.confirm = [scarlet]경고![]\n이 작업은 저장된 맵, 맵파일, 잠금 해제된 목록과 키 매핑, 그리고 모든 데이터를 삭제합니다.\n확인 버튼을 다시 눌러 모든 데이터를 삭제하고 게임에서 나갑니다. settings.clearall.confirm = [scarlet]경고![]\n이 작업은 저장된 맵, 맵파일, 잠금 해제된 목록과 조작키 설정, 그리고 모든 데이터를 삭제합니다.\n확인 버튼을 다시 눌러 모든 데이터를 삭제하고 게임에서 나갑니다.
settings.clearsaves.confirm = 정말로 모든 저장된 파일들을 삭제하시겠습니까? settings.clearsaves.confirm = 정말로 모든 저장된 파일들을 삭제하시겠습니까?
settings.clearsaves = 저장 초기화 settings.clearsaves = 저장 초기화
settings.clearresearch = 연구 초기화 settings.clearresearch = 연구 초기화
@@ -628,7 +629,7 @@ yes = O
no = X no = X
info.title = 정보 info.title = 정보
error.title = [scarlet]오류가 발생했습니다. error.title = [scarlet]오류가 발생했습니다.
error.crashtitle = 오류가 발생했습니다 error.crashtitle = 오류가 발생했습니다.
unit.nobuild = [scarlet]건설 불가 unit.nobuild = [scarlet]건설 불가
lastaccessed = [lightgray]마지막 조작: {0} lastaccessed = [lightgray]마지막 조작: {0}
block.unknown = [lightgray]??? block.unknown = [lightgray]???
@@ -698,9 +699,9 @@ stat.abilities = 능력
stat.canboost = 부스터 stat.canboost = 부스터
stat.flying = 비행 stat.flying = 비행
ability.forcefield = 수호 역장 ability.forcefield = 보호막 필드
ability.repairfield = 수리 파동 ability.repairfield = 수리 필드
ability.statusfield = 강화 오오라 ability.statusfield = 상태이상 필드
ability.unitspawn = {0} 공장 ability.unitspawn = {0} 공장
ability.shieldregenfield = 방어막 복구 필드 ability.shieldregenfield = 방어막 복구 필드
ability.movelightning = 가속 전격 ability.movelightning = 가속 전격
@@ -837,7 +838,7 @@ uiscale.reset = UI 스케일이 변경되었습니다.\n"확인"버튼을 눌러
uiscale.cancel = 취소 후 나가기 uiscale.cancel = 취소 후 나가기
setting.bloom.name = 화려한 효과 setting.bloom.name = 화려한 효과
keybind.title = 조작키 설정 keybind.title = 조작키 설정
keybinds.mobile = [scarlet]대부분의 키 맵핑은 모바일에서 작동하지 않습니다. 기본 이동만 지원됩니다. keybinds.mobile = [scarlet]대부분의 조작키 설정은 모바일에서 작동하지 않습니다. 기본 이동만 지원됩니다.
category.general.name = 일반 category.general.name = 일반
category.view.name = 보기 category.view.name = 보기
category.multiplayer.name = 멀티플레이어 category.multiplayer.name = 멀티플레이어
@@ -992,8 +993,8 @@ unit.quasar.name = 퀘이사
unit.crawler.name = 크롤러 unit.crawler.name = 크롤러
unit.atrax.name = 아트락스 unit.atrax.name = 아트락스
unit.spiroct.name = 스피록트 unit.spiroct.name = 스피록트
unit.arkyid.name = 알카이 unit.arkyid.name = 아키
unit.toxopid.name = 톡소피드 unit.toxopid.name = 톡소피드
unit.flare.name = 플레어 unit.flare.name = 플레어
unit.horizon.name = 호라이즌 unit.horizon.name = 호라이즌
unit.zenith.name = 제니스 unit.zenith.name = 제니스
@@ -1005,7 +1006,7 @@ unit.mega.name = 메가
unit.quad.name = 쿼드 unit.quad.name = 쿼드
unit.oct.name = 옥트 unit.oct.name = 옥트
unit.risso.name = 리소 unit.risso.name = 리소
unit.minke.name = unit.minke.name =
unit.bryde.name = 브라이드 unit.bryde.name = 브라이드
unit.sei.name = 세이 unit.sei.name = 세이
unit.omura.name = 오무라 unit.omura.name = 오무라
@@ -1013,9 +1014,9 @@ unit.alpha.name = 알파
unit.beta.name = 베타 unit.beta.name = 베타
unit.gamma.name = 감마 unit.gamma.name = 감마
unit.scepter.name = 셉터 unit.scepter.name = 셉터
unit.reign.name = unit.reign.name = 레인
unit.vela.name = 벨라 unit.vela.name = 벨라
unit.corvus.name = 코르 unit.corvus.name = 코르
block.resupply-point.name = 보급 지점 block.resupply-point.name = 보급 지점
block.parallax.name = 패럴랙스 block.parallax.name = 패럴랙스
@@ -1058,7 +1059,7 @@ block.deepwater.name = 깊은 물
block.water.name = block.water.name =
block.tainted-water.name = 오염된 물 block.tainted-water.name = 오염된 물
block.darksand-tainted-water.name = 오염된 젖은 검은 모래 block.darksand-tainted-water.name = 오염된 젖은 검은 모래
block.tar.name = 석유 block.tar.name = 타르
block.stone.name = 바위 block.stone.name = 바위
block.sand.name = 모래 block.sand.name = 모래
block.darksand.name = 검은 모래 block.darksand.name = 검은 모래
@@ -1198,7 +1199,7 @@ block.cyclone.name = 사이클론
block.fuse.name = 퓨즈 block.fuse.name = 퓨즈
block.shock-mine.name = 전격 지뢰 block.shock-mine.name = 전격 지뢰
block.overdrive-projector.name = 과부하 프로젝터 block.overdrive-projector.name = 과부하 프로젝터
block.force-projector.name = 포스 프로젝터 block.force-projector.name = 보호막 프로젝터
block.arc.name = 아크 block.arc.name = 아크
block.rtg-generator.name = RTG 발전기 block.rtg-generator.name = RTG 발전기
block.spectre.name = 스펙터 block.spectre.name = 스펙터
@@ -1257,7 +1258,7 @@ hint.placeDrill = 드릴을 설치하려면 오른쪽 아래의 \ue85e [accent]
hint.placeDrill.mobile = 오른쪽 아래 메뉴의 \ue85e [accent]드릴[]을 선택하고, \uf870 [accent]드릴[] 를 선택해서 구리 광석 위를 누르십시오.\n\n설치를 완료하려면 오른쪽 아래의 \ue800 [accent]완료 버튼[]을 누르십시오. hint.placeDrill.mobile = 오른쪽 아래 메뉴의 \ue85e [accent]드릴[]을 선택하고, \uf870 [accent]드릴[] 를 선택해서 구리 광석 위를 누르십시오.\n\n설치를 완료하려면 오른쪽 아래의 \ue800 [accent]완료 버튼[]을 누르십시오.
hint.placeConveyor = 컨베이어는 아이템을 드릴에서 다른 블록으로 이동시켜줍니다. \ue814 [accent]분배[] 카테고리에서 \uf896 [accent]컨베이어[]를 선택하십시오.\n\n클릭하거나 드래그로 다수의 컨베이어를 설치할 수 있습니다.\n클릭하고 놓지 않은채로 마우스 [accent]휠을 돌리면 돌아갑니다. hint.placeConveyor = 컨베이어는 아이템을 드릴에서 다른 블록으로 이동시켜줍니다. \ue814 [accent]분배[] 카테고리에서 \uf896 [accent]컨베이어[]를 선택하십시오.\n\n클릭하거나 드래그로 다수의 컨베이어를 설치할 수 있습니다.\n클릭하고 놓지 않은채로 마우스 [accent]휠을 돌리면 돌아갑니다.
hint.placeConveyor.mobile = 컨베이어는 아이템을 드릴에서 다른 블록으로 이동시켜줍니다. \ue814 [accent]분배[] 카테고리에서 \uf896 [accent]컨베이어[]를 선택하십시오.\n\n여러개의 컨베이어를 놓으려면 손가락으로 누른채로 끌어서 설치 범위를 지정하십시오. hint.placeConveyor.mobile = 컨베이어는 아이템을 드릴에서 다른 블록으로 이동시켜줍니다. \ue814 [accent]분배[] 카테고리에서 \uf896 [accent]컨베이어[]를 선택하십시오.\n\n여러개의 컨베이어를 놓으려면 손가락으로 누른채로 끌어서 설치 범위를 지정하십시오.
hint.placeTurret = 적에게서 당신의 기지를 막아내려면 \uf861 [accent]포탑[]를 설치하십시오.\n\n포탑 탄약 필요 - 지금은 \uf838구리가 필요합니다.\n컨베이어를 사용해 드릴에 구리를 공급하십시오. hint.placeTurret = 적에게서 당신의 기지를 막아내려면 \uf861 [accent]포탑[]를 설치하십시오.\n\n포탑 탄약 필요 - 지금은 \uf838 구리가 필요합니다.\n컨베이어를 사용해 드릴에 구리를 공급하십시오.
hint.breaking = 블록을 부수려면 [accent]오른클릭[]이나 드래그를 하십시오. hint.breaking = 블록을 부수려면 [accent]오른클릭[]이나 드래그를 하십시오.
hint.breaking.mobile = 블럭을 부수려면 오른쪽 아래의 \ue817 [accent]망치[]를 눌러 해체 모드를 활성화 하십시오.\n\n손가락으로 누른채로 끌어서 해체 범위를 지정하십시오. hint.breaking.mobile = 블럭을 부수려면 오른쪽 아래의 \ue817 [accent]망치[]를 눌러 해체 모드를 활성화 하십시오.\n\n손가락으로 누른채로 끌어서 해체 범위를 지정하십시오.
hint.research = 새 기술을 연구하려면 \ue875 [accent]연구[]버튼을 누르십시오. hint.research = 새 기술을 연구하려면 \ue875 [accent]연구[]버튼을 누르십시오.
@@ -1279,9 +1280,12 @@ hint.payloadDrop.mobile = 다시 내려놓으려면 빈 공간에서 [accent]화
hint.waveFire = [accent]Wave[]포탑에 탄약으로 물을 넣으면 주변의 불을 자동으로 꺼줍니다. hint.waveFire = [accent]Wave[]포탑에 탄약으로 물을 넣으면 주변의 불을 자동으로 꺼줍니다.
hint.generator = \uf879 [accent]화력 발전기[]는 석탄을 태워서 주변 블록에 전력을 전달합니다.\n\n \uf87f 더 넓은 범위의 블록에 전력을 전달하려면 [accent]Power Nodes[]를 사용하십시오. hint.generator = \uf879 [accent]화력 발전기[]는 석탄을 태워서 주변 블록에 전력을 전달합니다.\n\n \uf87f 더 넓은 범위의 블록에 전력을 전달하려면 [accent]Power Nodes[]를 사용하십시오.
hint.guardian = [accent]수호자[] 유닛들은 방어력을 가집니다. [accent]구리[]와 [accent]납[]같은 약한 탄약으로는 [scarlet]아무런 효과도 없습니다[].\n\n그런 수호자를 없애려면 높은 단계의 포탑 또는 \uf835 [accent]흑연[]을 탄약으로 넣은 \uf861듀오/\uf859살보를 사용하십시오. hint.guardian = [accent]수호자[] 유닛들은 방어력을 가집니다. [accent]구리[]와 [accent]납[]같은 약한 탄약으로는 [scarlet]아무런 효과도 없습니다[].\n\n그런 수호자를 없애려면 높은 단계의 포탑 또는 \uf835 [accent]흑연[]을 탄약으로 넣은 \uf861듀오/\uf859살보를 사용하십시오.
hint.coreUpgrade = 코어는 [accent]상위 코어를 위에 설치함[]으로써 업그레이드될 수 있습니다.\n\n [accent]기반[] 코어를  [accent]조각[] 코어 위에 설치하십시오. 주변에 장애물이 없는지도 확인하십시오.
hint.presetLaunch = [accent]얼어붙은 숲[]과 같은 회색[accent]캠페인 지역[]은 어디에서나 출격해서 올 수 있습니다. 주변 지역을 점령하지 않아도 됩니다.\n\n이와 같은 [accent]네임드 지역[]들은 [accent]선택적[]입니다.
hint.coreIncinerate = 코어가 아이템으로 가득 찬 후에 받는 모든 아이템들은 [accent]소각[]될 것입니다.
item.copper.description = 가장 기본적인 건설 재료. 모든 유형의 블록에서 광범위하게 사용됩니다. item.copper.description = 가장 기본적인 건설 재료. 모든 유형의 블록에서 광범위하게 사용됩니다.
item.copper.details = 평범한 구리. 세르로에 비정상적으로 많이 분포되어 있습니다. 별다른 보강재 없이는 구조적 문제 때문에 내구성이 비교적 약합니다. item.copper.details = 평범한 구리. 세르로에 비정상적으로 많이 분포되어 있습니다. 별다른 보강재 없이는 구조적 문제 때문에 내구성이 비교적 약합니다.
item.lead.description = 기본 초반 재료. 전자 및 액체 수송 블록에서 광범위하게 사용되는 자원입니다. item.lead.description = 기본 초반 재료. 전자 및 액체 수송 블록에서 광범위하게 사용되는 자원입니다.
item.lead.details = 밀도가 높으며 반응성이 적은 자원. 배터리에 주로 사용됩니다. item.lead.details = 밀도가 높으며 반응성이 적은 자원. 배터리에 주로 사용됩니다.
item.metaglass.description = 초강력 방탄유리. 액체 분배 및 저장에 광범위하게 사용됩니다. item.metaglass.description = 초강력 방탄유리. 액체 분배 및 저장에 광범위하게 사용됩니다.
@@ -1398,7 +1402,7 @@ block.laser-drill.description = 레이저 기술을 통해 더욱 빠르게 채
block.blast-drill.description = 최상위 드릴. 많은 양의 전력이 필요합니다. block.blast-drill.description = 최상위 드릴. 많은 양의 전력이 필요합니다.
block.water-extractor.description = 지하수를 추출합니다. 물을 구하기 어려운 곳에서 사용합니다. block.water-extractor.description = 지하수를 추출합니다. 물을 구하기 어려운 곳에서 사용합니다.
block.cultivator.description = 대기 중의 작은 농도의 포자를 산업용 포자로 배양합니다. block.cultivator.description = 대기 중의 작은 농도의 포자를 산업용 포자로 배양합니다.
block.cultivator.details = 재발견된 기술. 가장 효율적으로 대량의 유기체를 생산할 때 사용된다. 과거, 세르플을 뒤덮은 포자의 최초 배양지로 판단된다. block.cultivator.details = 재발견된 기술. 가장 효율적으로 대량의 유기체를 생산할 때 사용된다. 과거, 세르플을 뒤덮은 포자의 최초 배양지로 판단된다.
block.oil-extractor.description = 석유를 추출하기 위해 많은 양의 전력과 모래 및 물을 사용합니다. block.oil-extractor.description = 석유를 추출하기 위해 많은 양의 전력과 모래 및 물을 사용합니다.
block.core-shard.description = 기지의 핵심입니다. 파괴되면 해당 지역과의 모든 연결이 끊어집니다. 이런 일이 일어나지 않도록 하십시오. block.core-shard.description = 기지의 핵심입니다. 파괴되면 해당 지역과의 모든 연결이 끊어집니다. 이런 일이 일어나지 않도록 하십시오.
block.core-shard.details = 첫 번째 버전. 휴대용. 자가복제 가능. 일회용 출격 추진기를 가졌으며, 행성간 이동에는 부적합함. block.core-shard.details = 첫 번째 버전. 휴대용. 자가복제 가능. 일회용 출격 추진기를 가졌으며, 행성간 이동에는 부적합함.
@@ -1410,23 +1414,23 @@ block.vault.description = 각 유형의 많은 양의 자원을 저장합니다.
block.container.description = 각 유형의 자원을 소량 저장합니다. 언로더 블록을 사용하여 컨테이너에서 자원을 빼낼 수 있습니다. block.container.description = 각 유형의 자원을 소량 저장합니다. 언로더 블록을 사용하여 컨테이너에서 자원을 빼낼 수 있습니다.
block.unloader.description = 근처의 비 수송 블록에서 자원을 빼냅니다. 눌러서 빼낼 자원을 변경할 수 있십니다. block.unloader.description = 근처의 비 수송 블록에서 자원을 빼냅니다. 눌러서 빼낼 자원을 변경할 수 있십니다.
block.launch-pad.description = 코어 출격 없이도 자원을 묶어 출격시킬 수 있습니다. block.launch-pad.description = 코어 출격 없이도 자원을 묶어 출격시킬 수 있습니다.
block.duo.description = 작고 저렴한 포탑. 지상 유닛에 유용합니다. block.duo.description = 적에게 탄환을 교대하며 발사합니다.
block.scatter.description = 저렴한 대공 포탑. 적군에게 납덩어리, 고철, 또는 강화 유리 조각 덩어리를 뿌립니다. block.scatter.description = 적군에게 납, 고철, 또는 강화 유리 조각 덩어리를 발사합니다.
block.scorch.description = 주변의 모든 적을 불태웁니다. 근거리에서 매우 효과적입니다. block.scorch.description = 주변의 모든 지상 적을 불태웁니다. 근거리에서 매우 효과적입니다.
block.hail.description = 작은 장거리 포병 포탑입니다. block.hail.description = 장거리에 걸쳐 지상 적에게 작은 포탄을 발사합니다.
block.wave.description = 중형 포탑. 적에게 액체를 발사합니다. 물이 공급되면 자동으로 화재를 진압합니다. block.wave.description = 적에게 액체 줄기를 발사합니다. 물이 공급되면 자동으로 화재를 진압합니다.
block.lancer.description = 중형 대지 레이저 포탑. 강력한 에너지 빔을 충전하여 발사합니다. block.lancer.description = 지상 목표물에게 강력한 에너지 빔을 충전하여 발사합니다.
block.arc.description = 작은 근거리 전격 포탑. 적에게 전격 아크를 발사합니다. block.arc.description = 지상 목표물에게 전격 아크를 발사합니다.
block.swarmer.description = 중형 미사일 포탑. 공중과 지상의 적을 모두 공격하며, 유도탄을 발사합니다. block.swarmer.description = 적에게 유도탄을 발사합니다.
block.salvo.description = 더 큰 고급 듀오 포탑입니다. 적에게 총알을 빠르게 발사합니다. block.salvo.description = 적에게 총알을 빠르게 일제히 발사합니다.
block.fuse.description = 넓은 근거리 파편 포탑. 근처의 적에게 3개의 관통 총알을 발사합니다. block.fuse.description = 주변 적에게 3개의 단거리 관통 레이저를 발사합니다.
block.ripple.description = 매우 강력한 포병 포탑. 원거리에 있는 적에게 포탄 무리를 쏘세요. block.ripple.description = 장거리에 걸쳐 지상 적에게 포탄 무리를 발사합니다.
block.cyclone.description = 대공 및 대지 포탑. 근처 적에게 폭발탄을 발사합니다. block.cyclone.description = 근처 적에게 폭발 파편 덩어리를 발사합니다.
block.spectre.description = 거대한 이중 배럴 대포. 공중 및 지상 목표물에 큰 관통 철갑탄을 발사합니다. block.spectre.description = 공중 및 지상 목표물에 큰 관통 철갑탄을 발사합니다.
block.meltdown.description = 거대한 레이저 대포. 근처의 적에게 지속적인 레이저 빔을 충전하여 발사합니다. 냉각가 있어야 작동합니다. block.meltdown.description = 주변 적에게 지속적인 레이저 빔을 충전하여 발사합니다. 냉각 액체가 있어야 작동합니다.
block.foreshadow.description = 거리에 걸친 거대한 단일 목표 볼트를 발사합니다. block.foreshadow.description = 거리에 걸친 거대한 단일 목표 저격탄을 발사합니다.
block.repair-point.description = 주변에서 가장 가까운 유닛을 지속적으로 치료합니다. block.repair-point.description = 인근에 가장 가까운 유닛을 지속적으로 치료합니다.
block.segment.description = 날아오는 발사체를 요격합니다. 레이저는 목표 대상이 아닙니다. block.segment.description = 날아오는 발사체를 요격합니다. 큰 발사체에겐 조준되지 않습니다.
block.parallax.description = 공중 목표물을 끌어오는 견인 광선을 발사하며, 견인 과정에서 데미지를 줍니다. block.parallax.description = 공중 목표물을 끌어오는 견인 광선을 발사하며, 견인 과정에서 데미지를 줍니다.
block.tsunami.description = 적에게 강력한 액체 줄기를 발사합니다. 물이 공급되면 자동으로 주변의 화재를 진압합니다. block.tsunami.description = 적에게 강력한 액체 줄기를 발사합니다. 물이 공급되면 자동으로 주변의 화재를 진압합니다.
block.silicon-crucible.description = 추가적으로 파이라타이트를 사용하여 더 높은 온도에서 석탄과 모래를 제련합니다. 뜨거운 곳에서 더 효율적입니다. block.silicon-crucible.description = 추가적으로 파이라타이트를 사용하여 더 높은 온도에서 석탄과 모래를 제련합니다. 뜨거운 곳에서 더 효율적입니다.
@@ -1465,8 +1469,8 @@ unit.corvus.description = 적에게 피해를 주고, 아군 구조물을 수리
unit.crawler.description = 적에게 달려들어서 거대한 폭발을 일으키는 자폭을 합니다. unit.crawler.description = 적에게 달려들어서 거대한 폭발을 일으키는 자폭을 합니다.
unit.atrax.description = 지상 목표물을 약화하는 광재 구체를 발사합니다. 대부분의 지형 위를 밟을 수 있습니다. unit.atrax.description = 지상 목표물을 약화하는 광재 구체를 발사합니다. 대부분의 지형 위를 밟을 수 있습니다.
unit.spiroct.description = 적에게 흡혈 레이저 빔을 발사하며, 흡혈을 통해 체력을 회복합니다. 대부분의 지형 위를 밟을 수 있습니다. unit.spiroct.description = 적에게 흡혈 레이저 빔을 발사하며, 흡혈을 통해 체력을 회복합니다. 대부분의 지형 위를 밟을 수 있습니다.
unit.arkyid.description = 적에게 흡혈 레이저 빔을 발사하며, 흡혈을 통해 체력을 회복합니다. 대부분의 지형 위를 밟을 수 있습니다. unit.arkyid.description = 적에게 흡혈 레이저 빔을 발사하며, 흡혈을 통해 체력을 회복합니다. 대부분의 지형 위를 밟을 수 있습니다.
unit.toxopid.description = 적에게 큰 전기 확산탄과 관통 레이저를 발사합니다. 대부분의 지형 위를 밟을 수 있습니다. unit.toxopid.description = 적에게 큰 전격 포탄 무리와 관통 레이저를 발사합니다. 대부분의 지형 위를 밟을 수 있습니다.
unit.flare.description = 지상 목표물에 일반적인 탄환을 발사합니다. unit.flare.description = 지상 목표물에 일반적인 탄환을 발사합니다.
unit.horizon.description = 지상 목표물에 폭탄을 투하합니다. unit.horizon.description = 지상 목표물에 폭탄을 투하합니다.
unit.zenith.description = 주변 모든 적에게 미사일을 살포합니다. unit.zenith.description = 주변 모든 적에게 미사일을 살포합니다.

View File

@@ -47,8 +47,6 @@ public class Vars implements Loadable{
public static final int bufferSize = 8192; public static final int bufferSize = 8192;
/** global charset, since Android doesn't support the Charsets class */ /** global charset, since Android doesn't support the Charsets class */
public static final Charset charset = Charset.forName("UTF-8"); public static final Charset charset = Charset.forName("UTF-8");
/** mods suggested for import */
public static final String[] suggestedMods = {""};
/** main application name, capitalized */ /** main application name, capitalized */
public static final String appName = "Mindustry"; public static final String appName = "Mindustry";
/** URL for itch.io donations. */ /** URL for itch.io donations. */

View File

@@ -36,7 +36,7 @@ public class BaseAI{
private static int correct = 0, incorrect = 0; private static int correct = 0, incorrect = 0;
private int lastX, lastY, lastW, lastH; private int lastX, lastY, lastW, lastH;
private boolean triedWalls; private boolean triedWalls, foundPath;
TeamData data; TeamData data;
Interval timer = new Interval(4); Interval timer = new Interval(4);
@@ -114,6 +114,9 @@ public class BaseAI{
} }
calcPath.add(calcTile.pos()); calcPath.add(calcTile.pos());
for(Point2 p : Geometry.d8){
calcPath.add(Point2.pack(p.x + calcTile.x, p.y + calcTile.y));
}
//found the end. //found the end.
if(calcTile.build instanceof CoreBuild b && b.team == state.rules.defaultTeam){ if(calcTile.build instanceof CoreBuild b && b.team == state.rules.defaultTeam){
@@ -125,6 +128,7 @@ public class BaseAI{
calcPath.clear(); calcPath.clear();
calcTile = null; calcTile = null;
totalCalcs ++; totalCalcs ++;
foundPath = true;
break; break;
} }
@@ -135,7 +139,7 @@ public class BaseAI{
} }
//only schedule when there's something to build. //only schedule when there's something to build.
if(totalCalcs > 0 && data.blocks.isEmpty() && timer.get(timerStep, Mathf.lerp(20f, 4f, data.team.rules().aiTier))){ if(foundPath && data.blocks.isEmpty() && timer.get(timerStep, Mathf.lerp(20f, 4f, data.team.rules().aiTier))){
if(!triedWalls){ if(!triedWalls){
tryWalls(); tryWalls();
triedWalls = true; triedWalls = true;

View File

@@ -154,22 +154,22 @@ public class TechTree implements ContentList{
}); });
node(kiln, Seq.with(new SectorComplete(craters)), () -> { node(kiln, Seq.with(new SectorComplete(craters)), () -> {
node(incinerator, () -> { node(pulverizer, () -> {
node(melter, () -> { node(incinerator, () -> {
node(surgeSmelter, () -> { node(melter, () -> {
node(surgeSmelter, () -> {
}); });
node(separator, () -> { node(separator, () -> {
node(pulverizer, () -> {
node(disassembler, () -> { node(disassembler, () -> {
}); });
}); });
});
node(cryofluidMixer, () -> { node(cryofluidMixer, () -> {
});
}); });
}); });
}); });
@@ -462,10 +462,11 @@ public class TechTree implements ContentList{
new Research(conduit), new Research(conduit),
new Research(wave) new Research(wave)
), () -> { ), () -> {
//TODO change positions?
node(impact0078, Seq.with( node(impact0078, Seq.with(
new SectorComplete(tarFields), new SectorComplete(tarFields),
new Research(Items.thorium), new Research(Items.thorium),
new Research(lancer),
new Research(salvo),
new Research(coreFoundation) new Research(coreFoundation)
), () -> { ), () -> {
node(desolateRift, Seq.with( node(desolateRift, Seq.with(

View File

@@ -1624,7 +1624,7 @@ public class UnitTypes implements ContentList{
xRand = 8f; xRand = 8f;
shotDelay = 1f; shotDelay = 1f;
bullet = new MissileBulletType(4.2f, 40){{ bullet = new MissileBulletType(4.2f, 42){{
homingPower = 0.12f; homingPower = 0.12f;
width = 8f; width = 8f;
height = 8f; height = 8f;
@@ -1634,7 +1634,7 @@ public class UnitTypes implements ContentList{
keepVelocity = false; keepVelocity = false;
splashDamageRadius = 35f; splashDamageRadius = 35f;
splashDamage = 45f; splashDamage = 45f;
lifetime = 56f; lifetime = 62f;
trailColor = Pal.bulletYellowBack; trailColor = Pal.bulletYellowBack;
backColor = Pal.bulletYellowBack; backColor = Pal.bulletYellowBack;
frontColor = Pal.bulletYellow; frontColor = Pal.bulletYellow;
@@ -1662,11 +1662,11 @@ public class UnitTypes implements ContentList{
shots = 3; shots = 3;
shotDelay = 4f; shotDelay = 4f;
inaccuracy = 1f; inaccuracy = 1f;
bullet = new BasicBulletType(7f, 55){{ bullet = new BasicBulletType(7f, 57){{
width = 13f; width = 13f;
height = 19f; height = 19f;
shootEffect = Fx.shootBig; shootEffect = Fx.shootBig;
lifetime = 30f; lifetime = 35f;
}}; }};
}}); }});
}}; }};

View File

@@ -10,21 +10,22 @@ import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.*;
import mindustry.audio.*; import mindustry.audio.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.content.TechTree.*; import mindustry.content.TechTree.*;
import mindustry.core.GameState.*; import mindustry.core.GameState.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
import mindustry.game.*;
import mindustry.game.Saves.*; import mindustry.game.Saves.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.input.*; import mindustry.input.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.io.SaveIO.*; import mindustry.io.SaveIO.*;
import mindustry.maps.*;
import mindustry.maps.Map; import mindustry.maps.Map;
import mindustry.maps.*;
import mindustry.net.*; import mindustry.net.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
@@ -340,7 +341,7 @@ public class Control implements ApplicationListener, Loadable{
state.rules.waves = true; state.rules.waves = true;
//reset win wave?? //reset win wave??
state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null ? sector.preset.captureWave : state.rules.winWave > state.wave ? state.rules.winWave : 40; state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null && sector.preset.captureWave > 0 ? sector.preset.captureWave : state.rules.winWave > state.wave ? state.rules.winWave : 40;
//if there's still an enemy base left, fix it //if there's still an enemy base left, fix it
if(state.rules.attackMode){ if(state.rules.attackMode){
@@ -506,6 +507,13 @@ public class Control implements ApplicationListener, Loadable{
settings.put("fullscreen", !full); settings.put("fullscreen", !full);
} }
if(Float.isNaN(Vars.player.x) || Float.isNaN(Vars.player.y)){
player.set(0, 0);
if(!player.dead()) player.unit().kill();
}
if(Float.isNaN(camera.position.x)) camera.position.x = world.unitWidth()/2f;
if(Float.isNaN(camera.position.y)) camera.position.y = world.unitHeight()/2f;
if(state.isGame()){ if(state.isGame()){
input.update(); input.update();

View File

@@ -123,7 +123,7 @@ public class Logic implements ApplicationListener{
}); });
//sync research //sync research
Events.on(ResearchEvent.class, e -> { Events.on(UnlockEvent.class, e -> {
if(net.server()){ if(net.server()){
Call.researched(e.content); Call.researched(e.content);
} }
@@ -243,8 +243,10 @@ public class Logic implements ApplicationListener{
} }
private void updateWeather(){ private void updateWeather(){
state.rules.weather.removeAll(w -> w.weather == null);
for(WeatherEntry entry : state.rules.weather){ for(WeatherEntry entry : state.rules.weather){
if(entry.weather == null) continue;
//update cooldown //update cooldown
entry.cooldown -= Time.delta; entry.cooldown -= Time.delta;

View File

@@ -615,6 +615,7 @@ public class World{
GenerateInput input = new GenerateInput(); GenerateInput input = new GenerateInput();
for(GenerateFilter filter : filters){ for(GenerateFilter filter : filters){
filter.randomize();
input.begin(filter, width(), height(), (x, y) -> tiles.getn(x, y)); input.begin(filter, width(), height(), (x, y) -> tiles.getn(x, y));
filter.apply(tiles, input); filter.apply(tiles, input);
} }

View File

@@ -6,6 +6,8 @@ import arc.graphics.g2d.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
@@ -42,6 +44,11 @@ public abstract class UnlockableContent extends MappableContent{
this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false); this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false);
} }
/** @return the tech node for this content. may be null. */
public @Nullable TechNode node(){
return TechTree.get(this);
}
public String displayDescription(){ public String displayDescription(){
return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName()); return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName());
} }

View File

@@ -49,7 +49,8 @@ public class ContinuousLaserBulletType extends BulletType{
@Override @Override
public float estimateDPS(){ public float estimateDPS(){
//assume firing duration is about 100 by default, may not be accurate there's no way of knowing in this method //assume firing duration is about 100 by default, may not be accurate there's no way of knowing in this method
return damage * 100f / 5f; //assume it pierces 3 blocks/units
return damage * 100f / 5f * 3f;
} }
@Override @Override

View File

@@ -40,9 +40,10 @@ public class LaserBulletType extends BulletType{
this(1f); this(1f);
} }
//assume it pierces at least 3 blocks
@Override @Override
public float estimateDPS(){ public float estimateDPS(){
return super.estimateDPS() * 2f; return super.estimateDPS() * 3f;
} }
@Override @Override

View File

@@ -197,7 +197,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
//region utility methods //region utility methods
public void addPlan(boolean checkPrevious){ public void addPlan(boolean checkPrevious){
if(!block.rebuildable) return; if(!block.rebuildable || (team == state.rules.defaultTeam && state.isCampaign() && !block.isVisible())) return;
if(self() instanceof ConstructBuild entity){ if(self() instanceof ConstructBuild entity){
//update block to reflect the fact that something was being constructed //update block to reflect the fact that something was being constructed
@@ -1099,8 +1099,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
public void displayBars(Table table){ public void displayBars(Table table){
for(Func<Building, Bar> bar : block.bars.list()){ for(Func<Building, Bar> bar : block.bars.list()){
table.add(bar.get(self())).growX(); //TODO fix conclusively
table.row(); try{
table.add(bar.get(self())).growX();
table.row();
}catch(ClassCastException e){
break;
}
} }
} }

View File

@@ -73,7 +73,7 @@ public class SectorInfo{
public boolean shown = false; public boolean shown = false;
/** Special variables for simulation. */ /** Special variables for simulation. */
public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope, bossHealth, bossDps; public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope, bossHealth, bossDps, curEnemyHealth, curEnemyDps;
/** Wave where first boss shows up. */ /** Wave where first boss shows up. */
public int bossWave = -1; public int bossWave = -1;
@@ -130,7 +130,7 @@ public class SectorInfo{
} }
//if there are infinite waves and no win wave, add a win wave. //if there are infinite waves and no win wave, add a win wave.
if(waves && winWave <= 0 && !attack){ if(winWave <= 0 && !attack){
winWave = 30; winWave = 30;
} }
@@ -229,8 +229,9 @@ public class SectorInfo{
//cap production/export by production //cap production/export by production
production.get(item).mean = Math.min(production.get(item).mean, rawProduction.get(item).mean); production.get(item).mean = Math.min(production.get(item).mean, rawProduction.get(item).mean);
if(export.containsKey(item)){ if(export.containsKey(item)){
export.get(item).mean = Math.min(export.get(item).mean, rawProduction.get(item).mean); export.get(item).mean = Math.min(export.get(item).mean, Math.max(rawProduction.get(item).mean, -production.get(item).mean));
} }
} }

View File

@@ -84,7 +84,7 @@ public class Universe{
if(state.hasSector()){ if(state.hasSector()){
//update sector light //update sector light
float light = state.getSector().getLight(); float light = state.getSector().getLight();
float alpha = Mathf.clamp(Mathf.map(light, 0f, 0.8f, 0.2f, 1f)); float alpha = Mathf.clamp(Mathf.map(light, 0f, 0.8f, 0.3f, 1f));
//assign and map so darkness is not 100% dark //assign and map so darkness is not 100% dark
state.rules.ambientLight.a = 1f - alpha; state.rules.ambientLight.a = 1f - alpha;

View File

@@ -836,7 +836,6 @@ public class MobileInput extends InputHandler implements GestureListener{
if(type == null) return; if(type == null) return;
boolean omni = unit.type.omniMovement; boolean omni = unit.type.omniMovement;
boolean ground = unit.isGrounded();
boolean allowHealing = type.canHeal; boolean allowHealing = type.canHeal;
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team && boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
((Building)target).damaged() && target.within(unit, type.range); ((Building)target).damaged() && target.within(unit, type.range);

View File

@@ -114,6 +114,7 @@ public abstract class LStatement{
t.actions(Actions.alpha(0), Actions.fadeIn(0.3f, Interp.fade)); t.actions(Actions.alpha(0), Actions.fadeIn(0.3f, Interp.fade));
t.top().pane(inner -> { t.top().pane(inner -> {
inner.marginRight(24f);
inner.top(); inner.top();
hideCons.get(inner, hide); hideCons.get(inner, hide);
}).top(); }).top();

View File

@@ -528,7 +528,9 @@ public class LStatements{
stack.clearChildren(); stack.clearChildren();
stack.addChild(tables[selected]); stack.addChild(tables[selected]);
t.pack();
t.parent.parent.pack();
t.parent.parent.invalidateHierarchy();
}).size(80f, 50f).growX().checked(selected == fi).group(group); }).size(80f, 50f).growX().checked(selected == fi).group(group);
} }
t.row(); t.row();

View File

@@ -73,6 +73,11 @@ public class SectorDamage{
enemyHealth += info.bossHealth; enemyHealth += info.bossHealth;
} }
if(i == waveBegin){
enemyDps += info.curEnemyDps;
enemyHealth += info.curEnemyHealth;
}
//happens due to certain regressions //happens due to certain regressions
if(enemyHealth < 0 || enemyDps < 0) continue; if(enemyHealth < 0 || enemyDps < 0) continue;
@@ -293,18 +298,19 @@ public class SectorDamage{
//skip player //skip player
if(unit.isPlayer()) continue; if(unit.isPlayer()) continue;
if(unit.team == state.rules.defaultTeam){ //scale health based on armor - yes, this is inaccurate, but better than nothing
//scale health based on armor - yes, this is inaccurate, but better than nothing float healthMult = 1f + Mathf.clamp(unit.armor / 20f);
float healthMult = 1f + Mathf.clamp(unit.armor / 20f);
if(unit.team == state.rules.defaultTeam){
sumHealth += unit.health*healthMult + unit.shield; sumHealth += unit.health*healthMult + unit.shield;
sumDps += unit.type.dpsEstimate; sumDps += unit.type.dpsEstimate;
if(unit.abilities.find(a -> a instanceof RepairFieldAbility) instanceof RepairFieldAbility h){ if(unit.abilities.find(a -> a instanceof RepairFieldAbility) instanceof RepairFieldAbility h){
sumRps += h.amount / h.reload * 60f; sumRps += h.amount / h.reload * 60f;
} }
}else{ }else{
curEnemyDps += unit.type.dpsEstimate; float bossMult = unit.isBoss() ? 3f : 1f;
curEnemyHealth += unit.health; curEnemyDps += unit.type.dpsEstimate * unit.damageMultiplier() * bossMult;
curEnemyHealth += unit.health * healthMult * unit.healthMultiplier() * bossMult + unit.shield;
} }
} }
@@ -316,12 +322,6 @@ public class SectorDamage{
for(int wave = state.wave; wave < state.wave + 10; wave ++){ for(int wave = state.wave; wave < state.wave + 10; wave ++){
float sumWaveDps = 0f, sumWaveHealth = 0f; float sumWaveDps = 0f, sumWaveHealth = 0f;
//first wave has to take into account current dps
if(wave == state.wave){
sumWaveDps += curEnemyDps;
sumWaveHealth += curEnemyHealth;
}
for(SpawnGroup group : state.rules.spawns){ for(SpawnGroup group : state.rules.spawns){
float healthMult = 1f + Mathf.clamp(group.type.armor / 20f); float healthMult = 1f + Mathf.clamp(group.type.armor / 20f);
StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect); StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect);
@@ -369,6 +369,11 @@ public class SectorDamage{
info.sumDps = sumDps * 1.05f; info.sumDps = sumDps * 1.05f;
info.sumRps = sumRps; info.sumRps = sumRps;
float cmult = 1.5f;
info.curEnemyDps = curEnemyDps*cmult;
info.curEnemyHealth = curEnemyHealth*cmult;
info.wavesSurvived = getWavesSurvived(info); info.wavesSurvived = getWavesSurvived(info);
} }

View File

@@ -23,24 +23,27 @@ public abstract class GenerateFilter{
//buffer of tiles used, each tile packed into a long struct //buffer of tiles used, each tile packed into a long struct
long[] buffer = new long[tiles.width * tiles.height]; long[] buffer = new long[tiles.width * tiles.height];
//save to buffer
for(int i = 0; i < tiles.width * tiles.height; i++){ for(int i = 0; i < tiles.width * tiles.height; i++){
Tile tile = tiles.geti(i); Tile tile = tiles.geti(i);
buffer[i] = PackTile.get(tile.blockID(), tile.floorID(), tile.overlayID());
in.apply(tile.x, tile.y, tile.block(), tile.floor(), tile.overlay());
apply();
buffer[i] = PackTile.get(in.block.id, in.floor.id, in.overlay.id);
} }
//write to buffer
for(int i = 0; i < tiles.width * tiles.height; i++){ for(int i = 0; i < tiles.width * tiles.height; i++){
Tile tile = tiles.geti(i); Tile tile = tiles.geti(i);
long b = buffer[i]; long b = buffer[i];
in.apply(tile.x, tile.y, Vars.content.block(PackTile.block(b)), Vars.content.block(PackTile.floor(b)), Vars.content.block(PackTile.overlay(b))); Block block = Vars.content.block(PackTile.block(b)), floor = Vars.content.block(PackTile.floor(b)), overlay = Vars.content.block(PackTile.overlay(b));
apply();
tile.setFloor(in.floor.asFloor()); tile.setFloor(floor.asFloor());
tile.setOverlay(!in.floor.asFloor().hasSurface() && in.overlay.asFloor().needsSurface ? Blocks.air : in.overlay); tile.setOverlay(!floor.asFloor().hasSurface() && overlay.asFloor().needsSurface ? Blocks.air : overlay);
if(!tile.block().synthetic() && !in.block.synthetic()){ if(!tile.block().synthetic() && !block.synthetic()){
tile.setBlock(in.block); tile.setBlock(block);
} }
} }
}else{ }else{

View File

@@ -151,8 +151,17 @@ public class BaseGenerator{
//clear path for ground units //clear path for ground units
for(Tile tile : cores){ for(Tile tile : cores){
Astar.pathfind(tile, spawn, t -> t.team() == state.rules.waveTeam && !t.within(tile, 25f * 8) ? 100000 : t.floor().hasSurface() ? 1 : 10, t -> !t.block().isStatic()).each(t -> { Astar.pathfind(tile, spawn, t -> t.team() == state.rules.waveTeam && !t.within(tile, 25f * 8) ? 100000 : t.floor().hasSurface() ? 1 : 10, t -> !t.block().isStatic()).each(t -> {
if(t.team() == state.rules.waveTeam && !t.within(tile, 25f * 8)){ if(!t.within(tile, 25f * 8)){
t.setBlock(Blocks.air); if(t.team() == state.rules.waveTeam){
t.setBlock(Blocks.air);
}
for(Point2 p : Geometry.d8){
Tile other = t.nearby(p);
if(other != null && other.team() == state.rules.waveTeam){
other.setBlock(Blocks.air);
}
}
} }
}); });
} }

View File

@@ -68,7 +68,7 @@ public class ArcNetProvider implements NetProvider{
} }
}); });
server = new Server(8192, 8192, new PacketSerializer()); server = new Server(32768, 8192, new PacketSerializer());
server.setMulticast(multicastGroup, multicastPort); server.setMulticast(multicastGroup, multicastPort);
server.setDiscoveryHandler((address, handler) -> { server.setDiscoveryHandler((address, handler) -> {
ByteBuffer buffer = NetworkIO.writeServerData(); ByteBuffer buffer = NetworkIO.writeServerData();

View File

@@ -67,7 +67,7 @@ public class Fonts{
} }
public static int cursorScale(){ public static int cursorScale(){
return Math.max(1, Mathf.round(Scl.scl(1f))); return 1;
} }
public static void loadFonts(){ public static void loadFonts(){

View File

@@ -356,43 +356,45 @@ public class JoinDialog extends BaseDialog{
continue; continue;
} }
Table[] groupTable = {null};
//table containing all groups //table containing all groups
global.table(g -> { for(String address : group.addresses){
for(String address : group.addresses){ String resaddress = address.contains(":") ? address.split(":")[0] : address;
String resaddress = address.contains(":") ? address.split(":")[0] : address; int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port;
int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port; net.pingHost(resaddress, resport, res -> {
net.pingHost(resaddress, resport, res -> { if(refreshes != cur) return;
if(refreshes != cur) return; res.port = resport;
res.port = resport;
//add header //add header
if(g.getChildren().isEmpty()){ if(groupTable[0] == null){
g.table(head -> { global.table(t -> groupTable[0] = t).row();
if(!group.name.isEmpty()){
head.add(group.name).color(Color.lightGray).padRight(4);
}
head.image().height(3f).growX().color(Color.lightGray);
//button for showing/hiding servers groupTable[0].table(head -> {
ImageButton[] image = {null}; if(!group.name.isEmpty()){
image[0] = head.button(hidden ? Icon.eyeOffSmall : Icon.eyeSmall, Styles.accenti, () -> { head.add(group.name).color(Color.lightGray).padRight(4);
group.setHidden(!group.hidden()); }
image[0].getStyle().imageUp = group.hidden() ? Icon.eyeOffSmall : Icon.eyeSmall; head.image().height(3f).growX().color(Color.lightGray);
if(group.hidden() && !showHidden){
g.remove();
}
}).size(40f).get();
image[0].addListener(new Tooltip(t -> t.background(Styles.black6).margin(4).label(() -> !group.hidden() ? "@server.shown" : "@server.hidden")));
}).width(targetWidth()).padBottom(-2).row();
}
addGlobalHost(res, g); //button for showing/hiding servers
ImageButton[] image = {null};
image[0] = head.button(hidden ? Icon.eyeOffSmall : Icon.eyeSmall, Styles.accenti, () -> {
group.setHidden(!group.hidden());
image[0].getStyle().imageUp = group.hidden() ? Icon.eyeOffSmall : Icon.eyeSmall;
if(group.hidden() && !showHidden){
groupTable[0].remove();
}
}).size(40f).get();
image[0].addListener(new Tooltip(t -> t.background(Styles.black6).margin(4).label(() -> !group.hidden() ? "@server.shown" : "@server.hidden")));
}).width(targetWidth()).padBottom(-2).row();
}
g.margin(5f); addGlobalHost(res, groupTable[0]);
g.pack();
}, e -> {}); groupTable[0].margin(5f);
} groupTable[0].pack();
}).row(); }, e -> {});
}
} }
} }
@@ -539,12 +541,14 @@ public class JoinDialog extends BaseDialog{
Log.info("Fetched @ community servers.", defaultServers.size); Log.info("Fetched @ community servers.", defaultServers.size);
}catch(Throwable e){ }catch(Throwable e){
Log.err("Failed to parse community servers."); Log.err("Failed to parse community servers.");
Log.err(e);
} }
}); });
}catch(Throwable e){ }catch(Throwable e){
Log.err("Failed to fetch community servers."); Log.err("Failed to fetch community servers.");
Log.err(e);
} }
}, t -> {}); }, Log::err);
} }
private void saveServers(){ private void saveServers(){

View File

@@ -6,9 +6,12 @@ import arc.files.*;
import arc.func.*; import arc.func.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.scene.style.*;
import arc.scene.ui.TextButton.*; import arc.scene.ui.TextButton.*;
import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.io.*; import arc.util.io.*;
import mindustry.ctype.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.mod.Mods.*; import mindustry.mod.Mods.*;
@@ -102,13 +105,9 @@ public class ModsDialog extends BaseDialog{
t.button("@mod.import.github", Icon.github, bstyle, () -> { t.button("@mod.import.github", Icon.github, bstyle, () -> {
dialog.hide(); dialog.hide();
var modString = Core.settings.getString("lastmod", "");
var suggested = Structs.random(suggestedMods);
ui.showTextInput("@mod.import.github", "", 64, modString.isEmpty() ? suggested : modString, text -> { ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> {
if(!modString.isEmpty() || !Structs.eq(suggested, text)){ Core.settings.put("lastmod", text);
Core.settings.put("lastmod", text);
}
ui.loadfrag.show(); ui.loadfrag.show();
//Try to download the 6.0 branch first, but if it doesn't exist, try master. //Try to download the 6.0 branch first, but if it doesn't exist, try master.
@@ -266,54 +265,73 @@ public class ModsDialog extends BaseDialog{
desc.add("@editor.description").padRight(10).color(Color.gray).top(); desc.add("@editor.description").padRight(10).color(Color.gray).top();
desc.row(); desc.row();
desc.add(mod.meta.description).growX().wrap().padTop(2); desc.add(mod.meta.description).growX().wrap().padTop(2);
desc.row();
} }
//TODO add this when mods work properly }).width(400f);
/*
Array<UnlockableContent> all = Array.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as(UnlockableContent.class); //TODO maybe enable later
if(false){
Seq<UnlockableContent> all = Seq.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as();
if(all.any()){ if(all.any()){
desc.add("@mod.content").padRight(10).color(Color.gray).top(); dialog.cont.row();
desc.row(); dialog.cont.pane(cs -> {
desc.pane(cs -> {
int i = 0; int i = 0;
for(UnlockableContent c : all){ for(UnlockableContent c : all){
cs.addImageButton(new TextureRegionDrawable(c.icon(Cicon.medium)), () -> { cs.button(new TextureRegionDrawable(c.icon(Cicon.medium)), Styles.cleari, Cicon.medium.size, () -> {
ui.content.show(c); ui.content.show(c);
}).size(50f).with(im -> {
var click = im.getClickListener();
im.update(() -> im.getImage().color.lerp(!click.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta));
}); });
if(++i % 8 == 0) cs.row(); if(++i % 8 == 0) cs.row();
} }
}).growX().minHeight(60f); }).growX().minHeight(60f);
}*/ }
}).width(400f); }
dialog.show(); dialog.show();
} }
private void githubImport(String branch, String repo, Cons<HttpStatus> err){ private void handleMod(String repo, HttpResponse result){
Core.net.httpGet("http://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> { try{
Core.net.httpGet(loc.getHeader("Location"), result -> { Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
if(result.getStatus() != HttpStatus.OK){ Streams.copy(result.getResultAsStream(), file.write(false));
err.get(result.getStatus()); mods.importMod(file);
}else{ file.delete();
try{ Core.app.post(() -> {
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip"); try{
Streams.copy(result.getResultAsStream(), file.write(false)); setup();
mods.importMod(file); ui.loadfrag.hide();
file.delete(); }catch(Throwable e){
Core.app.post(() -> { ui.showException(e);
try{
setup();
ui.loadfrag.hide();
}catch(Throwable e){
ui.showException(e);
}
});
}catch(Throwable e){
modError(e);
}
} }
}, t2 -> Core.app.post(() -> modError(t2))); });
}catch(Throwable e){
modError(e);
}
}
private void githubImport(String branch, String repo, Cons<HttpStatus> err){
Core.net.httpGet("https://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> {
if(loc.getStatus() == HttpStatus.OK){
if(loc.getHeader("Location") != null){
Core.net.httpGet(loc.getHeader("Location"), result -> {
if(result.getStatus() != HttpStatus.OK){
err.get(result.getStatus());
}else{
handleMod(repo, result);
}
}, t2 -> Core.app.post(() -> modError(t2)));
}else{
handleMod(repo, loc);
}
}else{
err.get(loc.getStatus());
}
}, t2 -> Core.app.post(() -> modError(t2))); }, t2 -> Core.app.post(() -> modError(t2)));
} }
} }

View File

@@ -15,8 +15,10 @@ import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.Objectives.*;
import mindustry.game.SectorInfo.*; import mindustry.game.SectorInfo.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -253,7 +255,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
boolean canSelect(Sector sector){ boolean canSelect(Sector sector){
if(mode == select) return sector.hasBase(); if(mode == select) return sector.hasBase();
//preset sectors can only be selected once unlocked //preset sectors can only be selected once unlocked
if(sector.preset != null) return sector.preset.unlocked() || sector.hasBase(); if(sector.preset != null){
TechNode node = sector.preset.node();
return node == null || node.parent == null || node.parent.content.unlocked();
}
return sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector return sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector
} }
@@ -291,7 +296,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
Color color = Color color =
sec.hasBase() ? Tmp.c2.set(Team.sharded.color).lerp(Team.crux.color, sec.hasEnemyBase() ? 0.5f : 0f) : sec.hasBase() ? Tmp.c2.set(Team.sharded.color).lerp(Team.crux.color, sec.hasEnemyBase() ? 0.5f : 0f) :
sec.preset != null ? Tmp.c2.set(Team.derelict.color).lerp(Color.white, Mathf.absin(Time.time, 10f, 1f)) : sec.preset != null ?
sec.preset.unlocked() ? Tmp.c2.set(Team.derelict.color).lerp(Color.white, Mathf.absin(Time.time, 10f, 1f)) :
Color.gray :
sec.hasEnemyBase() ? Team.crux.color : sec.hasEnemyBase() ? Team.crux.color :
null; null;
@@ -352,7 +359,12 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
for(Sector sec : planet.sectors){ for(Sector sec : planet.sectors){
if(sec != hovered){ if(sec != hovered){
var preficon = sec.icon(); var preficon = sec.icon();
var icon = (sec.isAttacked() ? Fonts.getLargeIcon("warning") : !sec.hasBase() && sec.preset != null && sec.preset.unlocked() && preficon == null ? Fonts.getLargeIcon("terrain") : preficon); var icon =
sec.isAttacked() ? Fonts.getLargeIcon("warning") :
!sec.hasBase() && sec.preset != null && sec.preset.unlocked() && preficon == null ?
Fonts.getLargeIcon("terrain") :
sec.preset != null && sec.preset.locked() && sec.preset.node() != null && !sec.preset.node().parent.content.locked() ? Fonts.getLargeIcon("lock") :
preficon;
var color = sec.preset != null && !sec.hasBase() ? Team.derelict.color : Team.sharded.color; var color = sec.preset != null && !sec.hasBase() ? Team.derelict.color : Team.sharded.color;
if(icon != null){ if(icon != null){
@@ -690,7 +702,21 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row(); stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row();
if(!sector.hasBase()){ boolean locked = sector.preset != null && sector.preset.locked() && sector.preset.node() != null;
if(locked){
stable.table(r -> {
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : sector.preset.node().objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
}).row();
}else if(!sector.hasBase()){
stable.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).row(); stable.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).row();
} }
@@ -730,13 +756,23 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){ if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){
stable.button(mode == select ? "@sectors.select" : sector.isBeingPlayed() ? "@sectors.resume" : sector.hasBase() ? "@sectors.go" : "@sectors.launch", Icon.play, () -> { stable.button(
mode == select ? "@sectors.select" :
sector.isBeingPlayed() ? "@sectors.resume" :
sector.hasBase() ? "@sectors.go" :
locked ? "@locked" : "@sectors.launch",
locked ? Icon.lock :Icon.play, () -> {
if(sector.isBeingPlayed()){ if(sector.isBeingPlayed()){
//already at this sector //already at this sector
hide(); hide();
return; return;
} }
if(sector.preset != null && sector.preset.locked()){
return;
}
boolean shouldHide = true; boolean shouldHide = true;
//save before launch. //save before launch.
@@ -778,7 +814,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
if(shouldHide) hide(); if(shouldHide) hide();
}).growX().height(54f).minWidth(170f).padTop(4); }).growX().height(54f).minWidth(170f).padTop(4).disabled(locked);
} }
stable.pack(); stable.pack();

View File

@@ -172,6 +172,7 @@ public class HintsFragment extends Fragment{
&& SectorPresets.frozenForest.unlocked() && SectorPresets.frozenForest.unlocked()
&& SectorPresets.frozenForest.sector.save == null, && SectorPresets.frozenForest.sector.save == null,
() -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest), () -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest),
coreIncinerate(() -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.core().storageCapacity - 10, () -> false),
; ;
@Nullable @Nullable

View File

@@ -255,7 +255,7 @@ public class Conveyor extends Block implements Autotiler{
} }
public boolean pass(Item item){ public boolean pass(Item item){
if(next != null && next.team == team && next.acceptItem(this, item)){ if(item != null && next != null && next.team == team && next.acceptItem(this, item)){
next.handleItem(this, item); next.handleItem(this, item);
return true; return true;
} }

View File

@@ -27,6 +27,8 @@ import java.util.zip.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class LogicBlock extends Block{ public class LogicBlock extends Block{
private static final int maxByteLen = 1024 * 500;
public int maxInstructionScale = 5; public int maxInstructionScale = 5;
public int instructionsPerTick = 1; public int instructionsPerTick = 1;
public float range = 8 * 10; public float range = 8 * 10;
@@ -137,6 +139,9 @@ public class LogicBlock extends Block{
stream.read(); stream.read();
int bytelen = stream.readInt(); int bytelen = stream.readInt();
if(bytelen > maxByteLen) throw new RuntimeException("Malformed logic data! Length: " + bytelen);
byte[] bytes = new byte[bytelen]; byte[] bytes = new byte[bytelen];
stream.readFully(bytes); stream.readFully(bytes);
@@ -197,6 +202,7 @@ public class LogicBlock extends Block{
int version = stream.read(); int version = stream.read();
int bytelen = stream.readInt(); int bytelen = stream.readInt();
if(bytelen > maxByteLen) throw new RuntimeException("Malformed logic data! Length: " + bytelen);
byte[] bytes = new byte[bytelen]; byte[] bytes = new byte[bytelen];
stream.readFully(bytes); stream.readFully(bytes);
@@ -312,9 +318,10 @@ public class LogicBlock extends Block{
//store any older variables //store any older variables
for(Var var : executor.vars){ for(Var var : executor.vars){
if(!var.constant){ boolean unit = var.name.equals("@unit");
if(!var.constant || unit){
BVar dest = asm.getVar(var.name); BVar dest = asm.getVar(var.name);
if(dest != null && !dest.constant){ if(dest != null && (!dest.constant || unit)){
dest.value = var.isobj ? var.objval : var.numval; dest.value = var.isobj ? var.objval : var.numval;
} }
} }

View File

@@ -88,23 +88,13 @@
"jre/lib/amd64/libdt_socket.so", "jre/lib/amd64/libdt_socket.so",
"jre/lib/amd64/libsplashscreen.so", "jre/lib/amd64/libsplashscreen.so",
"jre/lib/amd64/libunpack.so", "jre/lib/amd64/libunpack.so",
"jre/lib/amd64/liblcms.so",
"jre/lib/amd64/libnpt.so", "jre/lib/amd64/libnpt.so",
"jre/lib/amd64/libmlib_image.so", "jre/lib/amd64/libmlib_image.so",
"jre/lib/amd64/libjsig.so",
"jre/lib/amd64/libinstrument.so", "jre/lib/amd64/libinstrument.so",
"jre/lib/amd64/libjaas_unix.so",
"jre/lib/amd64/libj2pcsc.so",
"jre/lib/amd64/libsaproc.so",
"jre/lib/amd64/libsunec.so",
"jre/lib/amd64/libj2pkcs11.so",
"jre/lib/amd64/libjsdt.so",
"jre/lib/amd64/libjdwp.so",
"jre/lib/amd64/libjava_crw_demo.so", "jre/lib/amd64/libjava_crw_demo.so",
"jre/lib/amd64/libfreetype.so", "jre/lib/amd64/libfreetype.so",
"jre/lib/amd64/libmanagement.so", "jre/lib/amd64/libmanagement.so",
"jre/lib/amd64/libsctp.so", "jre/lib/amd64/libsctp.so",
"jre/lib/amd64/libj2gss.so",
"jre/lib/amd64/libjpeg.so", "jre/lib/amd64/libjpeg.so",
"jre/lib/amd64/libfreetype.so.6", "jre/lib/amd64/libfreetype.so.6",
"jre/lib/amd64/libjsoundalsa.so", "jre/lib/amd64/libjsoundalsa.so",

View File

@@ -1,6 +1,7 @@
package mindustry.desktop.steam; package mindustry.desktop.steam;
import arc.*; import arc.*;
import arc.math.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import com.codedisaster.steamworks.*; import com.codedisaster.steamworks.*;
@@ -99,13 +100,13 @@ public class SStats implements SteamUserStatsCallback{
for(Sector sec : planet.sectors){ for(Sector sec : planet.sectors){
if(sec.hasBase()){ if(sec.hasBase()){
for(var v : sec.info.production.values()){ for(var v : sec.info.production.values()){
total += v.mean; if(v.mean > 0) total += v.mean * 60;
} }
} }
} }
} }
SStat.maxProduction.max((int)total); SStat.maxProduction.max(Mathf.round(total));
}); });
Events.run(Trigger.newGame, () -> Core.app.post(() -> { Events.run(Trigger.newGame, () -> Core.app.post(() -> {

View File

@@ -0,0 +1,8 @@
[This is a truncated changelog, see Github for full notes]
- Added disclaimer for community servers
- Added hints concerning core upgrades and numbered sectors
- Decreased spore storm opacity
- Made items stay inside reconstructors when rebuilding (Contributed by @Quezler)
- Made early game procedural sectors slightly easier
- Fixed some blocks having duplicate research dependencies
- Fixed legacy research not being cleared after first import

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Leaving sectors while guardians are present is now heavily penalized
- Made community servers sort by ping
- Changed "global items" to "total items"
- Added hint about core item incineration
- Made sector landing zones display on map when visible but not unlocked

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=3ed298e35dafc98667875c397c7cb0d0f1fcbc07 archash=d9f2b846e51e511aa4985c32297f6cdc8552ca87

View File

@@ -58,6 +58,7 @@ public class ApplicationTests{
add(netServer = new NetServer()); add(netServer = new NetServer());
content.init(); content.init();
} }
@Override @Override
@@ -77,6 +78,10 @@ public class ApplicationTests{
} }
Thread.sleep(10); Thread.sleep(10);
} }
Block block = content.getByName(ContentType.block, "build2");
assertEquals("build2", block == null ? null : block.name, "2x2 construct block doesn't exist?");
}catch(Throwable r){ }catch(Throwable r){
fail(r); fail(r);
} }
@@ -496,8 +501,8 @@ public class ApplicationTests{
void buildingOverlap(){ void buildingOverlap(){
initBuilding(); initBuilding();
Builderc d1 = (Builderc)UnitTypes.poly.create(Team.sharded); Unit d1 = UnitTypes.poly.create(Team.sharded);
Builderc d2 = (Builderc)UnitTypes.poly.create(Team.sharded); Unit d2 = UnitTypes.poly.create(Team.sharded);
//infinite build range //infinite build range
state.rules.editor = true; state.rules.editor = true;
@@ -523,8 +528,8 @@ public class ApplicationTests{
void buildingDestruction(){ void buildingDestruction(){
initBuilding(); initBuilding();
Builderc d1 = (Builderc)UnitTypes.poly.create(Team.sharded); Builderc d1 = UnitTypes.poly.create(Team.sharded);
Builderc d2 = (Builderc)UnitTypes.poly.create(Team.sharded); Builderc d2 = UnitTypes.poly.create(Team.sharded);
d1.set(10f, 20f); d1.set(10f, 20f);
d2.set(10f, 20f); d2.set(10f, 20f);

View File

@@ -34,7 +34,7 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{
public void createGenerator(InputType inputType){ public void createGenerator(InputType inputType){
Vars.state = new GameState(); Vars.state = new GameState();
Vars.state.rules = new Rules(); Vars.state.rules = new Rules();
generator = new ItemLiquidGenerator(inputType != InputType.liquids, inputType != InputType.items, "fakegen"){ generator = new ItemLiquidGenerator(inputType != InputType.liquids, inputType != InputType.items, "fakegen" + System.nanoTime()){
{ {
powerProduction = 0.1f; powerProduction = 0.1f;
itemDuration = fakeItemDuration; itemDuration = fakeItemDuration;

View File

@@ -28,35 +28,42 @@ public class PowerTestFixture{
headless = true; headless = true;
Core.graphics = new FakeGraphics(); Core.graphics = new FakeGraphics();
Core.files = new MockFiles(); Core.files = new MockFiles();
Vars.content = new ContentLoader(){
@Override
public void handleMappableContent(MappableContent content){
} boolean make = content == null;
};
if(make){
Vars.content = new ContentLoader(){
@Override
public void handleMappableContent(MappableContent content){
}
};
}
Vars.state = new GameState(); Vars.state = new GameState();
Vars.tree = new FileTree(); Vars.tree = new FileTree();
content.createBaseContent(); if(make){
content.createBaseContent();
}
Log.useColors = false; Log.useColors = false;
Time.setDeltaProvider(() -> 0.5f); Time.setDeltaProvider(() -> 0.5f);
} }
protected static PowerGenerator createFakeProducerBlock(float producedPower){ protected static PowerGenerator createFakeProducerBlock(float producedPower){
return new PowerGenerator("fakegen"){{ return new PowerGenerator("fakegen" + System.nanoTime()){{
buildType = () -> new GeneratorBuild(); buildType = () -> new GeneratorBuild();
powerProduction = producedPower; powerProduction = producedPower;
}}; }};
} }
protected static Battery createFakeBattery(float capacity){ protected static Battery createFakeBattery(float capacity){
return new Battery("fakebattery"){{ return new Battery("fakebattery" + System.nanoTime()){{
buildType = () -> new BatteryBuild(); buildType = () -> new BatteryBuild();
consumes.powerBuffered(capacity); consumes.powerBuffered(capacity);
}}; }};
} }
protected static Block createFakeDirectConsumer(float powerPerTick){ protected static Block createFakeDirectConsumer(float powerPerTick){
return new PowerBlock("fakedirectconsumer"){{ return new PowerBlock("fakedirectconsumer" + System.nanoTime()){{
buildType = Building::create; buildType = Building::create;
consumes.power(powerPerTick); consumes.power(powerPerTick);
}}; }};