Compare commits

...

55 Commits
v110 ... v111

Author SHA1 Message Date
Anuken
baf6bdf6a5 Increased sector damage calculation radius 2020-10-25 17:21:34 -04:00
Anuken
889fd64656 arc 2020-10-25 15:55:34 -04:00
Anuken
59b5d809db Balance 2020-10-25 15:55:23 -04:00
Anuken
6c3372e71c Slightly more reasonable waves 2020-10-25 15:07:51 -04:00
Anuken
1cdea49383 Merge remote-tracking branch 'origin/master' 2020-10-25 12:35:52 -04:00
Anuken
a6307039b0 Fixed some nullability issues 2020-10-25 12:35:48 -04:00
Anuken
aaacf244d2 Merge pull request #3096 from azureblue/placement-direction
make ending direction follow the path
2020-10-25 12:28:41 -04:00
Anuken
2a0af620be Sector land announcement 2020-10-25 12:00:57 -04:00
Anuken
b801093597 Merge remote-tracking branch 'origin/master' 2020-10-25 11:22:19 -04:00
Anuken
81dae580a4 Bugfixes / Removed tile unit multi-mining 2020-10-25 11:22:10 -04:00
Anuken
03f58a1b02 Merge pull request #2972 from sk7725/patch-20
Add colors to StatusEffects
2020-10-25 10:35:42 -04:00
Anuken
b2b0082c7d Fixed #3108 / Fixed #3112 2020-10-25 09:23:13 -04:00
Anuken
1d85495f7a Merge remote-tracking branch 'origin/master' 2020-10-24 19:06:09 -04:00
Anuken
41d388aa7f Item sprite cleanup 2020-10-24 19:06:05 -04:00
Anuken
e88921c77e Merge pull request #3100 from MEEPofFaith/patch-1
"diable"
2020-10-24 16:56:49 -04:00
MEEP of Faith
705728f16a "diable"
oh no
2020-10-24 13:49:30 -07:00
azureblue
a7c24d4bfd fixing formatting 2020-10-24 22:10:02 +02:00
Anuken
79ace277ed Merge remote-tracking branch 'origin/master' 2020-10-24 15:55:25 -04:00
Anuken
bc035f9490 Fixed #3095 2020-10-24 15:55:20 -04:00
azureblue
73a5af2e5e make ending direction follow the path 2020-10-24 21:34:47 +02:00
Anuken
ee7bdbf44e Merge pull request #3081 from genNAowl/standardize-liquid-stats
Remove timePeriod in LiquidBaseValue
2020-10-24 15:18:47 -04:00
Anuken
b611726c3e Merge remote-tracking branch 'origin/master' 2020-10-24 15:11:31 -04:00
Anuken
819686e812 Unit factory production list 2020-10-24 15:11:27 -04:00
Anuken
94e40a32b9 Merge pull request #3093 from DeltaNedas/patch-11
add more info to print object
2020-10-24 15:09:50 -04:00
DeltaNedas
3b2669fa21 pattern variables? 2020-10-24 17:23:54 +00:00
DeltaNedas
801ff1701e silly 2020-10-24 17:19:50 +00:00
DeltaNedas
364a2b43c2 add Content fallback 2020-10-24 17:04:58 +00:00
DeltaNedas
279d898d68 add more info to print object 2020-10-24 16:37:55 +00:00
Anuken
a930b34c3e Merge pull request #3090 from QmelZ/master
Turkish Translation
2020-10-24 11:58:21 -04:00
QmelZ
232a3dfe87 one last little fix 2020-10-24 17:28:10 +03:00
Anuken
ddbca62f92 Merge remote-tracking branch 'origin/master' 2020-10-24 10:22:04 -04:00
Anuken
646b022d38 Bugfixes 2020-10-24 10:21:57 -04:00
QmelZ
19ef414e5c even more fixes 2020-10-24 17:18:56 +03:00
QmelZ
f46eb24615 some other fixes 2020-10-24 17:07:52 +03:00
Anuken
4e6faf3bac Merge pull request #3083 from genNAowl/titanium-ammo-fuse
Switch places of titanium and thorium ammo in `Blocks.java`
2020-10-24 09:47:21 -04:00
Anuken
48f5007306 Merge pull request #2990 from driver1748/patch-5
Update bundle_zh_CN.properties
2020-10-24 09:46:09 -04:00
Anuken
56acbcecfd Merge branch 'master' into patch-5 2020-10-24 09:46:02 -04:00
QmelZ
7d0952d858 small fix 2020-10-24 15:59:28 +03:00
QmelZ
ef40a2e78e Update bundle_tr.properties 2020-10-24 15:56:51 +03:00
QmelZ
b4abd3b23d Merge pull request #1 from SilentOwl476/patch-1
Update bundle_tr.properties
2020-10-24 09:10:32 +03:00
Leonwang4234
f2a984f3e7 Lets hope this compiles because I don't have time to test it 2020-10-23 19:53:21 -07:00
Anuken
1ffa3f21f0 (2) 2020-10-23 20:41:32 -04:00
Anuken
7e323f1ebf Logic locate fix 2020-10-23 20:38:33 -04:00
SilentOwl476
bff79d7c27 Update bundle_tr.properties 2020-10-24 01:46:02 +03:00
Anuken
117f002545 Fixed #3082 2020-10-23 18:24:18 -04:00
Anuken
953c1889b5 Fixed #3080 2020-10-23 17:18:49 -04:00
Leonwang4234
cc024a0bac remove timePeriod in LiquidBaseValue 2020-10-23 13:34:07 -07:00
Anuken
42bfab6e40 Fixed #3079 2020-10-23 16:12:36 -04:00
Anuken
c8ef7ebafb Fixed #3078 + action listeners for player control events 2020-10-23 15:59:52 -04:00
Anuken
5512c1817d Extra scrap for debris field map 2020-10-23 14:09:05 -04:00
WinterUnderTheSnow
877999dd1e Update bundle_zh_CN.properties 2020-10-20 07:13:10 +08:00
WinterUnderTheSnow
699f2ca5a2 Update bundle_zh_CN.properties
Confirmed units,name this may be the final version of translation by main translating team for v108
2020-10-17 17:38:12 +08:00
Sunny Kim
89f04ed3e9 oh no 2020-10-17 01:22:07 +09:00
Sunny Kim
956840b50b oops 2020-10-16 20:59:40 +09:00
Sunny Kim
b79e0373f4 Add colors to StatusEffects 2020-10-16 20:12:54 +09:00
99 changed files with 707 additions and 535 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 481 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 B

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 B

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 519 B

View File

@@ -581,7 +581,6 @@ settings.clearcampaignsaves.confirm = Are you sure you want to clear all of your
paused = [accent]< Paused > paused = [accent]< Paused >
clear = Clear clear = Clear
banned = [scarlet]Banned banned = [scarlet]Banned
unplaceable.sectorcaptured = [scarlet]Requires captured sector
yes = Yes yes = Yes
no = No no = No
info.title = Info info.title = Info
@@ -727,6 +726,7 @@ setting.blockreplace.name = Automatic Block Suggestions
setting.linear.name = Linear Filtering setting.linear.name = Linear Filtering
setting.hints.name = Hints setting.hints.name = Hints
setting.flow.name = Display Resource Flow Rate setting.flow.name = Display Resource Flow Rate
setting.backgroundpause.name = Pause In Background
setting.buildautopause.name = Auto-Pause Building setting.buildautopause.name = Auto-Pause Building
setting.animatedwater.name = Animated Surfaces setting.animatedwater.name = Animated Surfaces
setting.animatedshields.name = Animated Shields setting.animatedshields.name = Animated Shields

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
credits.text = 作者[royal]Anuken[] - [sky]anukendev@gmail.com[] 译者[orange]老滑稽[] - [cyan]QQ1290419934[] credits.text = 作者[royal]Anuken[] - [sky]anukendev@gmail.com[]
credits = 致谢 credits = 致谢
contributors = 翻译者和贡献者 contributors = 翻译者和贡献者
discord = 加入 Mindustry 的 Discord discord = 加入 Mindustry 的 Discord
@@ -20,8 +20,8 @@ gameover = 游戏结束
gameover.pvp = [accent] {0}[]队获胜! gameover.pvp = [accent] {0}[]队获胜!
highscore = [accent]新纪录! highscore = [accent]新纪录!
copied = 已复制。 copied = 已复制。
indev.popup = [accent]v6[]仍在[accent]测试版[].\n[lightgray]这意味着:[]\n[scarlet]- 战役不完善[]\n- 内容不完整\n - 大多[scarlet]单位AI[]运行不佳\n- 单位系统不完整\n- 一切内容都可能发生变动或调整。\n\n[accent]主群QQ681962751[]提交错误报告。 indev.popup = [accent]6.0[]仍在[accent]测试版[].\n[lightgray]这意味着:[]\n[scarlet]- 战役玩法完全没有完成[]\n- 很多内容还没有做完\n - 大多[scarlet]单位AI[]无法正确地运行\n- 单位系统完全没有完成\n- 一切您所看到的内容都可能会移除或调整。\n\n[accent]Github[]提交错误报告。\n[#66ccff]来自译者WinterUnderTheSnow的忠告不建议新玩家游玩还在测试阶段的6.0!建议您先从[orange]v104.6[#66ccff]或[orange]v104.10[#66ccff]开始游玩!
indev.notready = 还没做好看NM indev.notready = 这部分玩法还未开发完成。
load.sound = 音乐加载中 load.sound = 音乐加载中
load.map = 地图加载中 load.map = 地图加载中
@@ -66,7 +66,7 @@ stat.delivered = 装运资源:
stat.playtime = 游玩时间:[accent] {0} stat.playtime = 游玩时间:[accent] {0}
stat.rank = 最终评级:[accent]{0} stat.rank = 最终评级:[accent]{0}
globalitems = [accent]Global Items globalitems = [accent]全局物品
map.delete = 确定要删除“[accent]{0}[]”地图吗? map.delete = 确定要删除“[accent]{0}[]”地图吗?
level.highscore = 最高分:[accent]{0} level.highscore = 最高分:[accent]{0}
level.select = 选择关卡 level.select = 选择关卡
@@ -115,7 +115,7 @@ mod.disable = 禁用
mod.content = 内容: mod.content = 内容:
mod.delete.error = 无法删除模组。可能文件被占用。 mod.delete.error = 无法删除模组。可能文件被占用。
mod.requiresversion = [scarlet]所需的游戏版本:[accent]{0} mod.requiresversion = [scarlet]所需的游戏版本:[accent]{0}
mod.outdated = [scarlet]模组不兼容6.0(缺失 minGameVersion: 105) mod.outdated = [scarlet]模组可能不能在6.0上正确地运行(缺失 minGameVersion: 105)
mod.missingdependencies = [scarlet]缺少前置模组:{0} mod.missingdependencies = [scarlet]缺少前置模组:{0}
mod.erroredcontent = [scarlet]内容错误 mod.erroredcontent = [scarlet]内容错误
mod.errors = 读取内容时发生错误. mod.errors = 读取内容时发生错误.
@@ -127,14 +127,14 @@ mod.reloadrequired = [scarlet]需要重启
mod.import = 导入模组 mod.import = 导入模组
mod.import.file = 导入文件 mod.import.file = 导入文件
mod.import.github = 从 GitHub 导入模组 mod.import.github = 从 GitHub 导入模组
mod.jarwarn = [scarlet]JAR模组存在危险性。[]\n请确保此模组来源安全可靠 mod.jarwarn = [scarlet]JAR模组注定存在危险性。[]\n请确保此模组来源安全可靠
mod.item.remove = 这个物品是[accent] '{0}'[]模组的一部分. 删除物品需要先卸载此模组. mod.item.remove = 这个物品是[accent] '{0}'[]模组的一部分. 删除物品需要先卸载此模组.
mod.remove.confirm = 此模组将被删除。 mod.remove.confirm = 此模组将被删除。
mod.author = [lightgray]作者:[] {0} mod.author = [lightgray]作者:[] {0}
mod.missing = 此存档包含您最近已更新或者现在未安装的模组。存档可能会损坏。确定要加载它吗?\n[lightgray]模组:\n{0} mod.missing = 此存档包含您最近已更新或者现在未安装的模组。存档可能会损坏。确定要加载它吗?\n[lightgray]模组:\n{0}
mod.preview.missing = 在创意工坊中发布此模组前,您必须添加一则预览图像。\n请将名为[accent] preview.png[] 的图像放入模组文件夹,然后重试。 mod.preview.missing = 在创意工坊中发布此模组前,您必须添加一则预览图像。\n请将名为[accent] preview.png[] 的图像放入模组文件夹,然后重试。
mod.folder.missing = 只有文件夹形式的模组能在创意工坊上发布。\n若要将任何模组转换为文件夹只需将其文件解压缩到文件夹中并删除旧压缩包然后重新启动游戏或重新加载模组。 mod.folder.missing = 只有文件夹形式的模组能在创意工坊上发布。\n若要将任何模组转换为文件夹只需将其文件解压缩到文件夹中并删除旧压缩包然后重新启动游戏或重新加载模组。
mod.scripts.disable = 的设备不支持含有脚本的模组。必须禁用相关模组以进入游戏。 mod.scripts.disable = 的设备不支持含有脚本的模组。必须禁用相关模组以进入游戏。
about.button = 关于 about.button = 关于
name = 名字: name = 名字:
@@ -157,22 +157,22 @@ server.closing = [accent]服务器关闭…
server.kicked.kick = 你被踢出了服务器。 server.kicked.kick = 你被踢出了服务器。
server.kicked.whitelist = 你不在服务器白名单中。 server.kicked.whitelist = 你不在服务器白名单中。
server.kicked.serverClose = 服务器已关闭。 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请稍后重新连接
server.kicked.nameInUse = 的名字与服务器中的一个人重复了。 server.kicked.nameInUse = 的名字与服务器中的一个人重复了。
server.kicked.nameEmpty = 无效的名字! server.kicked.nameEmpty = 无效的名字!
server.kicked.idInUse = 已经连接了这个服务器!不允许在一台电脑上用两个客户端连接。 server.kicked.idInUse = 已经连接了这个服务器!不允许在一台设备上用两个客户端连接。
server.kicked.customClient = 这个服务器不支持自定义客户端。请下载官方版本。 server.kicked.customClient = 这个服务器不支持自定义客户端。请下载官方版本。
server.kicked.gameover = 游戏结束! server.kicked.gameover = 游戏结束!
server.kicked.serverRestarting = 服务器正在重启. server.kicked.serverRestarting = 服务器正在重启.
server.versions = 客户端版本:[accent] {0}[]\n服务器版本[accent] {1}[] server.versions = 客户端版本:[accent] {0}[]\n服务器版本[accent] {1}[]
host.info = [accent]创建局域网游戏[]按钮会在[scarlet] 6567 []端口运行一个服务器。[]\n任何在同一个[lightgray] Wi-Fi 或本地网络[]下的人应该都可以在服务器列表中看到你的服务器。\n\n如果你想让别人在任何地方都能通过 IP 地址连接,你需要设定[accent]端口转发[]。\n\n[lightgray]注意:如果某人无法连接到你的局域网游戏,请确保你在防火墙设置里允许了 Mindustry 访问本地网络。 host.info = [accent]创建局域网游戏[]按钮会在[scarlet] 6567 []端口运行一个服务器。[]\n任何在同一个[lightgray] Wi-Fi 或本地网络[]下的人应该都可以在服务器列表中看到你的服务器。\n\n如果你想让别人在任何地方都能通过 IP 地址连接,你需要设定[accent]端口转发[]。\n\n[lightgray]注意:如果某人无法连接到你的局域网游戏,请确保你在防火墙设置里允许了 Mindustry 访问本地网络。
join.info = 您可以输入[accent]服务器的 IP 地址[]来连接,或寻找[accent]本地网络[]中的服务器来连接。\n支持局域网或广域网的多人游戏。\n\n[lightgray]注意:没有全球服务器列表;如果你想通过 IP 地址连接某个服务器,你需要向主询问 IP 地址。 join.info = 您可以输入[accent]服务器的 IP 地址[]来连接,或寻找[accent]本地网络[]中的服务器来连接。\n支持局域网或广域网的多人游戏。\n\n[lightgray]注意:没有全球服务器列表;如果你想通过 IP 地址连接某个服务器,你需要向主询问 IP 地址。
hostserver = 创建服务器 hostserver = 创建服务器
invitefriends = 邀请朋友 invitefriends = 邀请朋友
hostserver.mobile = 创建\n服务器 hostserver.mobile = 创建\n服务器
@@ -201,7 +201,7 @@ server.bans.none = 没有被封禁的玩家!
server.admins = 管理员 server.admins = 管理员
server.admins.none = 该服务器没有管理员! server.admins.none = 该服务器没有管理员!
server.add = 添加服务器 server.add = 添加服务器
server.delete = 确定要删除这个服务器吗? server.delete = 确定要删除这个服务器吗?
server.edit = 编辑服务器 server.edit = 编辑服务器
server.outdated = [crimson]服务器过旧![] server.outdated = [crimson]服务器过旧![]
server.outdated.client = [crimson]客户端过旧![] server.outdated.client = [crimson]客户端过旧![]
@@ -228,11 +228,11 @@ server.addressinuse = 地址已在使用!
server.invalidport = 无效的端口! server.invalidport = 无效的端口!
server.error = [crimson]创建服务器错误:[accent]{0} server.error = [crimson]创建服务器错误:[accent]{0}
save.new = 新存档 save.new = 新存档
save.overwrite = 确定要覆盖这个存档吗? save.overwrite = 确定要覆盖这个存档吗?
overwrite = 覆盖 overwrite = 覆盖
save.none = 没有找到存档! save.none = 没有找到存档!
savefail = 保存失败! savefail = 保存失败!
save.delete.confirm = 确定要删除这个存档吗? save.delete.confirm = 确定要删除这个存档吗?
save.delete = 删除 save.delete = 删除
save.export = 导出存档 save.export = 导出存档
save.import.invalid = [accent]此存档无效! save.import.invalid = [accent]此存档无效!
@@ -245,7 +245,7 @@ save.rename.text = 新名称:
selectslot = 选择一个存档。 selectslot = 选择一个存档。
slot = [accent]存档位 {0} slot = [accent]存档位 {0}
editmessage = 编辑消息 editmessage = 编辑消息
save.corrupted = [accent]存档损坏或无效!\n如果刚刚升级了游戏,那么这可能是因为存档格式改变了,而[scarlet]不是[] bug 。 save.corrupted = [accent]存档损坏或无效!\n如果刚刚升级了游戏,那么这可能是因为存档格式改变了,而[scarlet]不是[] bug 。
empty = < 空 > empty = < 空 >
on = on =
off = off =
@@ -297,8 +297,8 @@ loadimage = 加载图片
saveimage = 保存图片 saveimage = 保存图片
unknown = 未知 unknown = 未知
custom = 自定义 custom = 自定义
builtin = builtin =
map.delete.confirm = 确定你想要删除这张地图吗?这个操作无法撤销! map.delete.confirm = 确定你想要删除这张地图吗?这个操作无法撤销!
map.random = [accent]随机地图 map.random = [accent]随机地图
map.nospawn = 这个地图没有核心!请在编辑器中添加一个[royal]己方[]的核心。 map.nospawn = 这个地图没有核心!请在编辑器中添加一个[royal]己方[]的核心。
map.nospawn.pvp = 这个地图没有敌人的核心!请在编辑器中添加一个[royal]敌人[]的核心。 map.nospawn.pvp = 这个地图没有敌人的核心!请在编辑器中添加一个[royal]敌人[]的核心。
@@ -363,7 +363,7 @@ editor.removeunit = 移除单位
editor.teams = 队伍 editor.teams = 队伍
editor.errorload = 读取文件出错:\n[accent]{0} editor.errorload = 读取文件出错:\n[accent]{0}
editor.errorsave = 保存文件出错:\n[accent]{0} editor.errorsave = 保存文件出错:\n[accent]{0}
editor.errorimage = 这是一幅图片,不是地图。请不要更改文件的扩展名来导入。\n\n如果想导入地图,请在编辑器中使用“导入地图”按钮。 editor.errorimage = 这是一幅图片,不是地图。请不要更改文件的扩展名来导入。\n\n如果想导入地图,请在编辑器中使用“导入地图”按钮。
editor.errorlegacy = 此地图太旧了,旧的地图格式已不再支持。 editor.errorlegacy = 此地图太旧了,旧的地图格式已不再支持。
editor.errornot = 这不是地图文件。 editor.errornot = 这不是地图文件。
editor.errorheader = 此地图文件无效或已损坏。 editor.errorheader = 此地图文件无效或已损坏。
@@ -376,8 +376,8 @@ editor.resize = 调整大小
editor.loadmap = 载入地图 editor.loadmap = 载入地图
editor.savemap = 保存地图 editor.savemap = 保存地图
editor.saved = 已保存! editor.saved = 已保存!
editor.save.noname = 的地图没有名字!在“地图信息”菜单里设置一个。 editor.save.noname = 的地图没有名字!在“地图信息”菜单里设置一个。
editor.save.overwrite = 的地图覆盖了一个内置的地图!在“地图信息”菜单里重新设置一个不同的名称。 editor.save.overwrite = 的地图覆盖了一个内置的地图!在“地图信息”菜单里重新设置一个不同的名称。
editor.import.exists = [scarlet]无法导入:[]存在名为“{0}”的内置地图! editor.import.exists = [scarlet]无法导入:[]存在名为“{0}”的内置地图!
editor.import = 导入… editor.import = 导入…
editor.importmap = 导入地图 editor.importmap = 导入地图
@@ -794,8 +794,8 @@ keybind.clear_building.name = 清除建筑
keybind.press = 请按一个键… keybind.press = 请按一个键…
keybind.press.axis = 请按一个轴或键… keybind.press.axis = 请按一个轴或键…
keybind.screenshot.name = 地图截图 keybind.screenshot.name = 地图截图
keybind.toggle_power_lines.name = 能量标识线 keybind.toggle_power_lines.name = 示/隐藏能量标识线
keybind.toggle_block_status.name = 方块状态 keybind.toggle_block_status.name = 示/隐藏方块状态
keybind.move_x.name = 水平移动 keybind.move_x.name = 水平移动
keybind.move_y.name = 竖直移动 keybind.move_y.name = 竖直移动
keybind.mouse_move.name = 跟随鼠标 keybind.mouse_move.name = 跟随鼠标
@@ -922,36 +922,36 @@ liquid.oil.name = 石油
liquid.cryofluid.name = 冷冻液 liquid.cryofluid.name = 冷冻液
unit.dagger.name = 尖刀 unit.dagger.name = 尖刀
unit.mace.name = 牙狼 unit.mace.name = 战锤
unit.fortress.name = 堡垒 unit.fortress.name = 堡垒
unit.nova.name = 新星 unit.nova.name = 新星
unit.pulsar.name = 脉冲 unit.pulsar.name =
unit.quasar.name = 超星 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 = 苍穹
unit.antumbra.name = unit.antumbra.name =
unit.eclipse.name = 日蚀 unit.eclipse.name = 日蚀
unit.mono.name = 独影 unit.mono.name = 独影
unit.poly.name = unit.poly.name =
unit.mega.name = 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 = 海神
unit.alpha.name = 阿尔法 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 = 死星
@@ -1056,7 +1056,7 @@ block.conveyor.name = 传送带
block.titanium-conveyor.name = 钛传送带 block.titanium-conveyor.name = 钛传送带
block.plastanium-conveyor.name = 塑钢传送带 block.plastanium-conveyor.name = 塑钢传送带
block.armored-conveyor.name = 装甲传送带 block.armored-conveyor.name = 装甲传送带
block.armored-conveyor.description = 运送物品,与钛传送带一样的速度,但有更强的装甲。除其他传送带,不接受任何边的输入。 block.armored-conveyor.description = 运送物品,与钛传送带一样的速度,但有更强的装甲。除其他传送带,不接受任何边的输入。
block.junction.name = 连接器 block.junction.name = 连接器
block.router.name = 路由器 block.router.name = 路由器
block.distributor.name = 分配器 block.distributor.name = 分配器
@@ -1148,26 +1148,26 @@ block.launch-pad.name = 发射台
block.launch-pad-large.name = 大型发射台 block.launch-pad-large.name = 大型发射台
block.segment.name = 裂解光束 block.segment.name = 裂解光束
block.command-center.name = 指挥中心 block.command-center.name = 指挥中心
block.ground-factory.name = 战单位工厂 block.ground-factory.name = 工厂
block.air-factory.name = 战单位工厂 block.air-factory.name = 工厂
block.naval-factory.name = 战单位工厂 block.naval-factory.name = 工厂
block.additive-reconstructor.name = 数增级单位重构工厂 block.additive-reconstructor.name = 添改单位重构工厂
block.multiplicative-reconstructor.name = 增级单位重构工厂 block.multiplicative-reconstructor.name = 倍单位重构工厂
block.exponential-reconstructor.name = 级单位重构工厂 block.exponential-reconstructor.name = 幂级单位重构工厂
block.tetrative-reconstructor.name = 无量级单位重构工厂 block.tetrative-reconstructor.name = 实验性泰坦重构工厂
block.payload-conveyor.name = 载荷传送带 block.payload-conveyor.name = 载荷传送带
block.payload-router.name = 载荷路由器 block.payload-router.name = 载荷路由器
block.disassembler.name = 离机 block.disassembler.name =
block.silicon-crucible.name = 热能坩埚 block.silicon-crucible.name = 坩埚
block.overdrive-dome.name = 超速投射器 block.overdrive-dome.name = 超速穹顶投射器
block.switch.name = 开关 block.switch.name = 开关
block.micro-processor.name = 微型处理器 block.micro-processor.name = 微型处理器
block.logic-processor.name = 逻辑处理器 block.logic-processor.name = 逻辑处理器
block.hyper-processor.name = 处理器 block.hyper-processor.name = 处理器
block.logic-display.name = 逻辑显示屏 block.logic-display.name = 逻辑显示屏
block.large-logic-display.name = 大型逻辑显示屏 block.large-logic-display.name = 大型逻辑显示屏
block.memory-cell.name = 存储单元 block.memory-cell.name = 内存单位
block.memory-bank.name = Memory Bank block.memory-bank.name = Memory Bank
team.blue.name = team.blue.name =

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 KiB

After

Width:  |  Height:  |  Size: 556 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -36,8 +36,8 @@ public class Vars implements Loadable{
public static boolean loadLocales = true; public static boolean loadLocales = true;
/** Whether the logger is loaded. */ /** Whether the logger is loaded. */
public static boolean loadedLogger = false, loadedFileLogger = false; public static boolean loadedLogger = false, loadedFileLogger = false;
/** Whether to show the cliff button in the editor*/ /** Whether to enable various experimental features (e.g. cliffs) */
public static boolean addCliffButton = false; public static boolean experimental = false;
/** Maximum extra padding around deployment schematics. */ /** Maximum extra padding around deployment schematics. */
public static final int maxLoadoutSchematicPad = 5; public static final int maxLoadoutSchematicPad = 5;
/** Maximum schematic size.*/ /** Maximum schematic size.*/

View File

@@ -312,6 +312,28 @@ public class BlockIndexer{
return null; return null;
} }
/** Find the closest ore block relative to a position. */
public Tile findClosestOre(Unit unit, Item item){
if(!(unit instanceof Minerc miner)) return null;
TileArray arr = getOrePositions(item);
arr.tiles.sort(t -> t.dst2(unit.x, unit.y));
for(Tile tile : arr.tiles){
for(int x = Math.max(0, tile.x - quadrantSize / 2); x < tile.x + quadrantSize / 2 && x < world.width(); x++){
for(int y = Math.max(0, tile.y - quadrantSize / 2); y < tile.y + quadrantSize / 2 && y < world.height(); y++){
Tile res = world.tile(x, y);
if(res.drop() == item && miner.validMine(res, false)){
return res;
}
}
}
}
return null;
}
/** @return extra unit cap of a team. This is added onto the base value. */ /** @return extra unit cap of a team. This is added onto the base value. */
public int getExtraUnits(Team team){ public int getExtraUnits(Team team){
return unitCaps[team.id]; return unitCaps[team.id];
@@ -457,8 +479,8 @@ public class BlockIndexer{
} }
public static class TileArray implements Iterable<Tile>{ public static class TileArray implements Iterable<Tile>{
private Seq<Tile> tiles = new Seq<>(false, 16); Seq<Tile> tiles = new Seq<>(false, 16);
private IntSet contained = new IntSet(); IntSet contained = new IntSet();
public void add(Tile tile){ public void add(Tile tile){
if(contained.add(tile.pos())){ if(contained.add(tile.pos())){

View File

@@ -14,7 +14,7 @@ import static mindustry.Vars.*;
public class LogicAI extends AIController{ public class LogicAI extends AIController{
/** Minimum delay between item transfers. */ /** Minimum delay between item transfers. */
public static final float transferDelay = 60f * 3f; public static final float transferDelay = 60f * 2f;
/** Time after which the unit resets its controlled and reverts to a normal unit. */ /** Time after which the unit resets its controlled and reverts to a normal unit. */
public static final float logicControlTimeout = 10f * 60f; public static final float logicControlTimeout = 10f * 60f;
@@ -44,8 +44,8 @@ public class LogicAI extends AIController{
@Override @Override
protected void updateMovement(){ protected void updateMovement(){
if(itemTimer > 0) itemTimer -= Time.delta; if(itemTimer >= 0) itemTimer -= Time.delta;
if(payTimer > 0) payTimer -= Time.delta; if(payTimer >= 0) payTimer -= Time.delta;
if(targetTimer > 0f){ if(targetTimer > 0f){
targetTimer -= Time.delta; targetTimer -= Time.delta;

View File

@@ -39,8 +39,8 @@ public class MinerAI extends AIController{
if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){ if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
mining = false; mining = false;
}else{ }else{
if(retarget() && targetItem != null){ if(timer.get(timerTarget, 60) && targetItem != null){
ore = indexer.findClosestOre(unit.x, unit.y, targetItem); ore = indexer.findClosestOre(unit, targetItem);
} }
if(ore != null){ if(ore != null){

View File

@@ -103,6 +103,7 @@ public class Blocks implements ContentList{
alwaysReplace = true; alwaysReplace = true;
hasShadow = false; hasShadow = false;
useColor = false; useColor = false;
wall = this;
} }
@Override public void drawBase(Tile tile){} @Override public void drawBase(Tile tile){}
@@ -1651,17 +1652,19 @@ public class Blocks implements ContentList{
float brange = range + 10f; float brange = range + 10f;
ammo( ammo(
Items.thorium, new ShrapnelBulletType(){{
length = brange;
damage = 105f;
ammoMultiplier = 5f;
}},
Items.titanium, new ShrapnelBulletType(){{ Items.titanium, new ShrapnelBulletType(){{
length = brange; length = brange;
damage = 66f; damage = 66f;
ammoMultiplier = 4f; ammoMultiplier = 4f;
width = 17f; width = 17f;
reloadMultiplier = 1.3f; reloadMultiplier = 1.3f;
}},
Items.thorium, new ShrapnelBulletType(){{
length = brange;
damage = 105f;
ammoMultiplier = 5f;
toColor = Pal.thoriumPink;
shootEffect = smokeEffect = Fx.thoriumShoot;
}} }}
); );
}}; }};
@@ -2034,7 +2037,7 @@ public class Blocks implements ContentList{
}}; }};
microProcessor = new LogicBlock("micro-processor"){{ microProcessor = new LogicBlock("micro-processor"){{
requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 50)); requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 30));
instructionsPerTick = 2; instructionsPerTick = 2;
@@ -2042,7 +2045,7 @@ public class Blocks implements ContentList{
}}; }};
logicProcessor = new LogicBlock("logic-processor"){{ logicProcessor = new LogicBlock("logic-processor"){{
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 100, Items.graphite, 60, Items.thorium, 50)); requirements(Category.logic, with(Items.lead, 320, Items.silicon, 60, Items.graphite, 60, Items.thorium, 50));
instructionsPerTick = 8; instructionsPerTick = 8;
@@ -2052,7 +2055,7 @@ public class Blocks implements ContentList{
}}; }};
hyperProcessor = new LogicBlock("hyper-processor"){{ hyperProcessor = new LogicBlock("hyper-processor"){{
requirements(Category.logic, with(Items.lead, 450, Items.silicon, 150, Items.thorium, 75, Items.surgeAlloy, 50)); requirements(Category.logic, with(Items.lead, 450, Items.silicon, 130, Items.thorium, 75, Items.surgeAlloy, 50));
consumes.liquid(Liquids.cryofluid, 0.08f); consumes.liquid(Liquids.cryofluid, 0.08f);
hasLiquids = true; hasLiquids = true;

View File

@@ -1217,7 +1217,15 @@ public class Fx{
randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> { randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f); lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
}); });
}),
thoriumShoot = new Effect(12f, e -> {
color(Color.white, Pal.thoriumPink, e.fin());
stroke(e.fout() * 1.2f + 0.5f);
randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
});
}), }),
reactorsmoke = new Effect(17, e -> { reactorsmoke = new Effect(17, e -> {

View File

@@ -17,6 +17,7 @@ public class SectorPresets implements ContentList{
groundZero = new SectorPreset("groundZero", serpulo, 15){{ groundZero = new SectorPreset("groundZero", serpulo, 15){{
alwaysUnlocked = true; alwaysUnlocked = true;
addStartingItems = true;
captureWave = 10; captureWave = 10;
difficulty = 1; difficulty = 1;
}}; }};

View File

@@ -6,6 +6,8 @@ import arc.math.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.graphics.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -18,6 +20,7 @@ public class StatusEffects implements ContentList{
none = new StatusEffect("none"); none = new StatusEffect("none");
burning = new StatusEffect("burning"){{ burning = new StatusEffect("burning"){{
color = Pal.lightFlame;
damage = 0.12f; //over 8 seconds, this would be 60 damage damage = 0.12f; //over 8 seconds, this would be 60 damage
effect = Fx.burning; effect = Fx.burning;
@@ -32,6 +35,7 @@ public class StatusEffects implements ContentList{
}}; }};
freezing = new StatusEffect("freezing"){{ freezing = new StatusEffect("freezing"){{
color = Color.valueOf("6ecdec");
speedMultiplier = 0.6f; speedMultiplier = 0.6f;
healthMultiplier = 0.8f; healthMultiplier = 0.8f;
effect = Fx.freezing; effect = Fx.freezing;
@@ -47,10 +51,12 @@ public class StatusEffects implements ContentList{
}}; }};
unmoving = new StatusEffect("unmoving"){{ unmoving = new StatusEffect("unmoving"){{
color = Pal.gray;
speedMultiplier = 0.001f; speedMultiplier = 0.001f;
}}; }};
slow = new StatusEffect("slow"){{ slow = new StatusEffect("slow"){{
color = Pal.lightishGray;
speedMultiplier = 0.4f; speedMultiplier = 0.4f;
}}; }};
@@ -80,6 +86,7 @@ public class StatusEffects implements ContentList{
}}; }};
melting = new StatusEffect("melting"){{ melting = new StatusEffect("melting"){{
color = Color.valueOf("ffa166");
speedMultiplier = 0.8f; speedMultiplier = 0.8f;
healthMultiplier = 0.8f; healthMultiplier = 0.8f;
damage = 0.3f; damage = 0.3f;
@@ -92,6 +99,7 @@ public class StatusEffects implements ContentList{
}}; }};
sapped = new StatusEffect("sapped"){{ sapped = new StatusEffect("sapped"){{
color = Pal.sap;
speedMultiplier = 0.7f; speedMultiplier = 0.7f;
healthMultiplier = 0.8f; healthMultiplier = 0.8f;
effect = Fx.sapped; effect = Fx.sapped;
@@ -99,12 +107,14 @@ public class StatusEffects implements ContentList{
}}; }};
sporeSlowed = new StatusEffect("spore-slowed"){{ sporeSlowed = new StatusEffect("spore-slowed"){{
color = Pal.spore;
speedMultiplier = 0.8f; speedMultiplier = 0.8f;
effect = Fx.sapped; effect = Fx.sapped;
effectChance = 0.04f; effectChance = 0.04f;
}}; }};
tarred = new StatusEffect("tarred"){{ tarred = new StatusEffect("tarred"){{
color = Color.valueOf("313131");
speedMultiplier = 0.6f; speedMultiplier = 0.6f;
effect = Fx.oily; effect = Fx.oily;
@@ -115,6 +125,7 @@ public class StatusEffects implements ContentList{
}}; }};
overdrive = new StatusEffect("overdrive"){{ overdrive = new StatusEffect("overdrive"){{
color = Pal.accent;
healthMultiplier = 0.95f; healthMultiplier = 0.95f;
speedMultiplier = 1.15f; speedMultiplier = 1.15f;
damageMultiplier = 1.4f; damageMultiplier = 1.4f;
@@ -124,6 +135,7 @@ public class StatusEffects implements ContentList{
}}; }};
overclock = new StatusEffect("overclock"){{ overclock = new StatusEffect("overclock"){{
color = Pal.accent;
speedMultiplier = 1.15f; speedMultiplier = 1.15f;
damageMultiplier = 1.15f; damageMultiplier = 1.15f;
reloadMultiplier = 1.25f; reloadMultiplier = 1.25f;
@@ -132,20 +144,27 @@ public class StatusEffects implements ContentList{
}}; }};
shielded = new StatusEffect("shielded"){{ shielded = new StatusEffect("shielded"){{
color = Pal.accent;
healthMultiplier = 3f; healthMultiplier = 3f;
}}; }};
boss = new StatusEffect("boss"){{ boss = new StatusEffect("boss"){{
color = Pal.health;
permanent = true; permanent = true;
damageMultiplier = 2f; damageMultiplier = 2f;
healthMultiplier = 2f; healthMultiplier = 2f;
}}; }};
shocked = new StatusEffect("shocked"); shocked = new StatusEffect("shocked"){{
color = Pal.lancerLaser;
}};
blasted = new StatusEffect("blasted"); blasted = new StatusEffect("blasted"){{
color = Color.valueOf("ff795e");
}};
corroded = new StatusEffect("corroded"){{ corroded = new StatusEffect("corroded"){{
color = Pal.plastanium;
damage = 0.1f; damage = 0.1f;
}}; }};
} }

View File

@@ -1200,7 +1200,7 @@ public class UnitTypes implements ContentList{
mineTier = 3; mineTier = 3;
health = 500; health = 500;
armor = 5f; armor = 5f;
speed = 2.3f; speed = 2.4f;
accel = 0.06f; accel = 0.06f;
drag = 0.017f; drag = 0.017f;
lowAltitude = true; lowAltitude = true;
@@ -1770,6 +1770,7 @@ public class UnitTypes implements ContentList{
health = 1; health = 1;
rotateSpeed = 360f; rotateSpeed = 360f;
itemCapacity = 0; itemCapacity = 0;
commandLimit = 0;
} }
@Override @Override

View File

@@ -39,7 +39,7 @@ public class Weathers implements ContentList{
minAlpha = 0f; minAlpha = 0f;
maxAlpha = 0.2f; maxAlpha = 0.2f;
density = 1500f; density = 1500f;
baseSpeed = 6.1f; baseSpeed = 5.4f;
attrs.set(Attribute.light, -0.1f); attrs.set(Attribute.light, -0.1f);
attrs.set(Attribute.water, -0.1f); attrs.set(Attribute.water, -0.1f);
opacityMultiplier = 0.8f; opacityMultiplier = 0.8f;

View File

@@ -181,6 +181,12 @@ public class Control implements ApplicationListener, Loadable{
Time.run(Fx.coreLand.lifetime, () -> { Time.run(Fx.coreLand.lifetime, () -> {
Fx.launch.at(core); Fx.launch.at(core);
Effect.shake(5f, 5f, core); Effect.shake(5f, 5f, core);
if(state.isCampaign()){
ui.announce("[accent]" + state.rules.sector.name() + "\n" +
(state.rules.sector.info.resources.any() ? "[lightgray]" + bundle.get("sectors.resources") + "[white] " +
state.rules.sector.info.resources.toString(" ", u -> u.emoji()) : ""), 5);
}
}); });
}); });
@@ -264,7 +270,6 @@ public class Control implements ApplicationListener, Loadable{
slot.load(); slot.load();
slot.setAutosave(true); slot.setAutosave(true);
state.rules.sector = sector; state.rules.sector = sector;
state.secinfo = state.rules.sector.info;
//if there is no base, simulate a new game and place the right loadout at the spawn position //if there is no base, simulate a new game and place the right loadout at the spawn position
if(state.rules.defaultTeam.cores().isEmpty()){ if(state.rules.defaultTeam.cores().isEmpty()){
@@ -282,8 +287,12 @@ public class Control implements ApplicationListener, Loadable{
//reset wave so things are more fair //reset wave so things are more fair
state.wave = 1; state.wave = 1;
//reset win wave??
state.rules.winWave = sector.preset != null ? sector.preset.captureWave : 40;
//kill all units, since they should be dead anwyay //kill all units, since they should be dead anwyay
Groups.unit.clear(); Groups.unit.clear();
Groups.fire.clear();
Tile spawn = world.tile(sector.info.spawnPosition); Tile spawn = world.tile(sector.info.spawnPosition);
Schematics.placeLaunchLoadout(spawn.x, spawn.y); Schematics.placeLaunchLoadout(spawn.x, spawn.y);
@@ -311,8 +320,8 @@ public class Control implements ApplicationListener, Loadable{
world.loadSector(sector); world.loadSector(sector);
state.rules.sector = sector; state.rules.sector = sector;
//assign origin when launching //assign origin when launching
state.secinfo.origin = origin; sector.info.origin = origin;
state.secinfo.destination = origin; sector.info.destination = origin;
logic.play(); logic.play();
control.saves.saveSector(sector); control.saves.saveSector(sector);
Events.fire(Trigger.newGame); Events.fire(Trigger.newGame);
@@ -403,13 +412,15 @@ public class Control implements ApplicationListener, Loadable{
@Override @Override
public void pause(){ public void pause(){
if(settings.getBool("backgroundpause", true)){
wasPaused = state.is(State.paused); wasPaused = state.is(State.paused);
if(state.is(State.playing)) state.set(State.paused); if(state.is(State.playing)) state.set(State.paused);
} }
}
@Override @Override
public void resume(){ public void resume(){
if(state.is(State.paused) && !wasPaused){ if(state.is(State.paused) && !wasPaused && settings.getBool("backgroundpause", true)){
state.set(State.playing); state.set(State.playing);
} }
} }

View File

@@ -26,8 +26,6 @@ public class GameState{
public GameStats stats = new GameStats(); public GameStats stats = new GameStats();
/** Global attributes of the environment, calculated by weather. */ /** Global attributes of the environment, calculated by weather. */
public Attributes envAttrs = new Attributes(); public Attributes envAttrs = new Attributes();
/** Sector information. Only valid in the campaign. */
public SectorInfo secinfo = new SectorInfo();
/** Team data. Gets reset every new game. */ /** Team data. Gets reset every new game. */
public Teams teams = new Teams(); public Teams teams = new Teams();
/** Number of enemies in the game; only used clientside in servers. */ /** Number of enemies in the game; only used clientside in servers. */

View File

@@ -55,15 +55,14 @@ public class Logic implements ApplicationListener{
} }
}); });
Events.on(LaunchItemEvent.class, e -> state.secinfo.handleItemExport(e.stack));
//when loading a 'damaged' sector, propagate the damage //when loading a 'damaged' sector, propagate the damage
Events.on(SaveLoadEvent.class, e -> { Events.on(SaveLoadEvent.class, e -> {
if(state.isCampaign()){ if(state.isCampaign()){
state.secinfo.write(); SectorInfo info = state.rules.sector.info;
info.write();
//how much wave time has passed //how much wave time has passed
int wavesPassed = state.secinfo.wavesPassed; int wavesPassed = info.wavesPassed;
//wave has passed, remove all enemies, they are assumed to be dead //wave has passed, remove all enemies, they are assumed to be dead
if(wavesPassed > 0){ if(wavesPassed > 0){
@@ -84,10 +83,10 @@ public class Logic implements ApplicationListener{
} }
//reset values //reset values
state.secinfo.damage = 0f; info.damage = 0f;
state.secinfo.wavesPassed = 0; info.wavesPassed = 0;
state.secinfo.hasCore = true; info.hasCore = true;
state.secinfo.secondsPassed = 0; info.secondsPassed = 0;
state.rules.sector.saveInfo(); state.rules.sector.saveInfo();
} }
@@ -273,7 +272,7 @@ public class Logic implements ApplicationListener{
state.teams.updateTeamStats(); state.teams.updateTeamStats();
if(state.isCampaign()){ if(state.isCampaign()){
state.secinfo.update(); state.rules.sector.info.update();
} }
if(state.isCampaign()){ if(state.isCampaign()){

View File

@@ -293,6 +293,13 @@ public class NetClient implements ApplicationListener{
setHudText(message); setHudText(message);
} }
@Remote(variants = Variant.both)
public static void announce(String message){
if(message == null) return;
ui.announce(message);
}
@Remote(variants = Variant.both) @Remote(variants = Variant.both)
public static void infoMessage(String message){ public static void infoMessage(String message){
if(message == null) return; if(message == null) return;

View File

@@ -378,6 +378,7 @@ public class UI implements ApplicationListener, Loadable{
cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center); cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center);
cont.row(); cont.row();
cont.button("@ok", this::hide).size(120, 50).pad(4); cont.button("@ok", this::hide).size(120, 50).pad(4);
closeOnBack();
}}.show(); }}.show();
} }
@@ -405,6 +406,7 @@ public class UI implements ApplicationListener, Loadable{
cont.button("@ok", this::hide).size(110, 50).fillX().left(); cont.button("@ok", this::hide).size(110, 50).fillX().left();
cont.row(); cont.row();
cont.add(col).colspan(2).pad(2); cont.add(col).colspan(2).pad(2);
closeOnBack();
}}.show(); }}.show();
} }
@@ -420,6 +422,7 @@ public class UI implements ApplicationListener, Loadable{
cont.add(text).width(400f).wrap().get().setAlignment(align, align); cont.add(text).width(400f).wrap().get().setAlignment(align, align);
cont.row(); cont.row();
buttons.button("@ok", this::hide).size(110, 50).pad(4); buttons.button("@ok", this::hide).size(110, 50).pad(4);
closeOnBack();
}}.show(); }}.show();
} }
@@ -427,6 +430,7 @@ public class UI implements ApplicationListener, Loadable{
new Dialog(titleText){{ new Dialog(titleText){{
cont.margin(15).add(text).width(400f).wrap().left().get().setAlignment(Align.left, Align.left); cont.margin(15).add(text).width(400f).wrap().left().get().setAlignment(Align.left, Align.left);
buttons.button("@ok", this::hide).size(110, 50).pad(4); buttons.button("@ok", this::hide).size(110, 50).pad(4);
closeOnBack();
}}.show(); }}.show();
} }
@@ -436,6 +440,7 @@ public class UI implements ApplicationListener, Loadable{
titleTable.row(); titleTable.row();
titleTable.image().color(Pal.accent).height(3f).growX().pad(2f); titleTable.image().color(Pal.accent).height(3f).growX().pad(2f);
buttons.button("@ok", this::hide).size(110, 50).pad(4); buttons.button("@ok", this::hide).size(110, 50).pad(4);
closeOnBack();
}}.show(); }}.show();
} }
@@ -487,13 +492,19 @@ public class UI implements ApplicationListener, Loadable{
dialog.show(); dialog.show();
} }
/** Display text in the middle of the screen, then fade out. */
public void announce(String text){ public void announce(String text){
Table t = new Table(); announce(text, 3);
}
/** Display text in the middle of the screen, then fade out. */
public void announce(String text, float duration){
Table t = new Table(Styles.black3);
t.touchable = Touchable.disabled; t.touchable = Touchable.disabled;
t.background(Styles.black3).margin(8f) t.margin(8f).add(text).style(Styles.outlineLabel).labelAlign(Align.center);
.add(text).style(Styles.outlineLabel).labelAlign(Align.center);
t.update(() -> t.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Align.center)); t.update(() -> t.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Align.center));
t.actions(Actions.fadeOut(3, Interp.pow4In), Actions.remove()); t.actions(Actions.fadeOut(duration, Interp.pow4In), Actions.remove());
t.pack();
Core.scene.add(t); Core.scene.add(t);
} }

View File

@@ -570,7 +570,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
}).growX().top(); }).growX().top();
} }
if(addCliffButton){ if(experimental){
mid.row(); mid.row();
mid.table(t -> { mid.table(t -> {

View File

@@ -34,9 +34,7 @@ public class WaveInfoDialog extends BaseDialog{
super("@waves.title"); super("@waves.title");
shown(this::setup); shown(this::setup);
hidden(() -> { hidden(() -> state.rules.spawns = groups);
state.rules.spawns = groups;
});
addCloseListener(); addCloseListener();
@@ -96,6 +94,14 @@ public class WaveInfoDialog extends BaseDialog{
view(1); view(1);
} }
}); });
if(experimental){
buttons.button("Random", Icon.refresh, () -> {
groups.clear();
groups = DefaultWaves.generate(1f / 10f);
updateWaves();
}).width(200f);
}
} }
void view(int amount){ void view(int amount){

View File

@@ -11,5 +11,6 @@ class GroupDefs<G>{
@GroupDef(value = Buildingc.class) G build; @GroupDef(value = Buildingc.class) G build;
@GroupDef(value = Syncc.class, mapping = true) G sync; @GroupDef(value = Syncc.class, mapping = true) G sync;
@GroupDef(value = Drawc.class) G draw; @GroupDef(value = Drawc.class) G draw;
@GroupDef(value = Firec.class) G fire;
@GroupDef(value = WeatherStatec.class) G weather; @GroupDef(value = WeatherStatec.class) G weather;
} }

View File

@@ -869,7 +869,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
public void placed(){ public void placed(){
if(net.client()) return; if(net.client()) return;
if((block.consumesPower && !block.outputsPower) || (!block.consumesPower && block.outputsPower)){ if(block.consumesPower || block.outputsPower){
int range = 10; int range = 10;
tempTiles.clear(); tempTiles.clear();
Geometry.circle(tileX(), tileY(), range, (x, y) -> { Geometry.circle(tileX(), tileY(), range, (x, y) -> {
@@ -1319,7 +1319,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
@Override @Override
public void control(LAccess type, Object p1, double p2, double p3, double p4){ public void control(LAccess type, Object p1, double p2, double p3, double p4){
if(type == LAccess.configure && block.logicConfigurable){
//change config only if it's new
Object prev = senseObject(LAccess.config);
if(prev != p1){
configureAny(p1);
}
}
} }
@Override @Override

View File

@@ -32,12 +32,16 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
} }
boolean mining(){ boolean mining(){
return mineTile != null && !(((Object)this) instanceof Builderc && ((Builderc)(Object)this).activelyBuilding()); return mineTile != null && !(((Object)this) instanceof Builderc b && b.activelyBuilding());
}
public boolean validMine(Tile tile, boolean checkDst){
return !(tile == null || tile.block() != Blocks.air || (!within(tile.worldx(), tile.worldy(), miningRange) && checkDst)
|| tile.drop() == null || !canMine(tile.drop())) && state.teams.canMine(self(), tile);
} }
public boolean validMine(Tile tile){ public boolean validMine(Tile tile){
return !(tile == null || tile.block() != Blocks.air || !within(tile.worldx(), tile.worldy(), miningRange) return validMine(tile, true);
|| tile.drop() == null || !canMine(tile.drop()));
} }
@Override @Override
@@ -58,6 +62,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
mineTile = null; mineTile = null;
mineTimer = 0f; mineTimer = 0f;
}else if(mining()){ }else if(mining()){
state.teams.registerMined(mineTile, self());
Item item = mineTile.drop(); Item item = mineTile.drop();
lookAt(angleTo(mineTile.worldx(), mineTile.worldy())); lookAt(angleTo(mineTile.worldx(), mineTile.worldy()));
mineTimer += Time.delta *type.mineSpeed; mineTimer += Time.delta *type.mineSpeed;
@@ -84,8 +89,6 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
mineTimer = 0f; mineTimer = 0f;
} }
} }
} }
} }

View File

@@ -78,6 +78,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
team = state.rules.defaultTeam; team = state.rules.defaultTeam;
admin = typing = false; admin = typing = false;
textFadeTime = 0f; textFadeTime = 0f;
x = y = 0f;
if(!dead()){ if(!dead()){
unit.controller(unit.type.createController()); unit.controller(unit.type.createController());
unit = Nulls.unit; unit = Nulls.unit;

View File

@@ -114,13 +114,14 @@ abstract class StatusComp implements Posc, Flyingc{
StatusEntry entry = statuses.get(index++); StatusEntry entry = statuses.get(index++);
entry.time = Math.max(entry.time - Time.delta, 0); entry.time = Math.max(entry.time - Time.delta, 0);
applied.set(entry.effect.id);
if(entry.time <= 0 && !entry.effect.permanent){ if(entry.effect == null || (entry.time <= 0 && !entry.effect.permanent)){
Pools.free(entry); Pools.free(entry);
index --; index --;
statuses.remove(index); statuses.remove(index);
}else{ }else{
applied.set(entry.effect.id);
speedMultiplier *= entry.effect.speedMultiplier; speedMultiplier *= entry.effect.speedMultiplier;
healthMultiplier *= entry.effect.healthMultiplier; healthMultiplier *= entry.effect.healthMultiplier;
damageMultiplier *= entry.effect.damageMultiplier; damageMultiplier *= entry.effect.damageMultiplier;

View File

@@ -160,7 +160,7 @@ public class AIController implements UnitController{
} }
protected boolean retarget(){ protected boolean retarget(){
return timer.get(timerTarget, 30); return timer.get(timerTarget, 40);
} }
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){ protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){

View File

@@ -262,9 +262,7 @@ public class DefaultWaves{
{dagger, mace, fortress, scepter, reign}, {dagger, mace, fortress, scepter, reign},
{nova, pulsar, quasar, vela, corvus}, {nova, pulsar, quasar, vela, corvus},
{crawler, atrax, spiroct, arkyid, toxopid}, {crawler, atrax, spiroct, arkyid, toxopid},
//{risso, minke, bryde, sei, omura}, //questionable choices {flare, horizon, rand.chance(0.2) && difficulty > 0.5 ? poly : zenith, rand.chance(0.5) ? quad : antumbra, rand.chance(0.1) ? quad : eclipse}
{flare, horizon, difficulty > 0.5 ? poly : zenith, quad, quad},
{flare, horizon, zenith, antumbra, eclipse}
}; };
//required progression: //required progression:
@@ -276,6 +274,7 @@ public class DefaultWaves{
int cap = 150; int cap = 150;
float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f; float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f;
float[] scaling = {1, 1, 1.5f, 3f, 4f};
Intc createProgression = start -> { Intc createProgression = start -> {
//main sequence //main sequence
@@ -284,18 +283,19 @@ public class DefaultWaves{
for(int i = start; i < cap;){ for(int i = start; i < cap;){
int f = i; int f = i;
int next = rand.random(8, 16); int next = rand.random(8, 16) + curTier * 4;
float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0); float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0);
int space = start == 0 ? 1 : rand.random(1, 2); int space = start == 0 ? 1 : rand.random(1, 2);
int ctier = curTier;
//main progression //main progression
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{ out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
unitAmount = f == 0 ? 1 : 10; unitAmount = f == start ? 1 : 6 / (int)scaling[ctier];
begin = f; begin = f;
end = f + next >= cap ? never : f + next; end = f + next >= cap ? never : f + next;
max = 14; max = 14;
unitScaling = rand.random(1f, 3f); unitScaling = (difficulty < 0.4f ? rand.random(2f, 4f) : rand.random(1f, 3f)) * scaling[ctier];
shields = shieldAmount; shields = shieldAmount;
shieldScaling = shieldsPerWave; shieldScaling = shieldsPerWave;
spacing = space; spacing = space;
@@ -303,18 +303,18 @@ public class DefaultWaves{
//extra progression that tails out, blends in //extra progression that tails out, blends in
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{ out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
unitAmount = 6; unitAmount = 3 / (int)scaling[ctier];
begin = f + next; begin = f + next - 1;
end = f + next + rand.random(8, 12); end = f + next + rand.random(6, 10);
max = 11; max = 6;
unitScaling = rand.random(2f); unitScaling = rand.random(1f, 2f);
spacing = rand.random(2, 3); spacing = rand.random(2, 4);
shields = shieldAmount; shields = shieldAmount/2f;
shieldScaling = shieldsPerWave; shieldScaling = shieldsPerWave;
}}); }});
i += next; i += next + 1;
if(curTier < 3 || rand.chance(0.2)){ if(curTier < 3 || rand.chance(0.05)){
curTier ++; curTier ++;
} }
@@ -330,11 +330,11 @@ public class DefaultWaves{
createProgression.get(0); createProgression.get(0);
int step = 5 + rand.random(3); int step = 5 + rand.random(5);
while(step <= cap){ while(step <= cap){
createProgression.get(step); createProgression.get(step);
step += (int)(rand.random(13, 25) * Mathf.lerp(1f, 0.5f, difficulty)); step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty));
} }
int bossWave = (int)(rand.random(30, 60) * Mathf.lerp(1f, 0.7f, difficulty)); int bossWave = (int)(rand.random(30, 60) * Mathf.lerp(1f, 0.7f, difficulty));

View File

@@ -38,7 +38,7 @@ public class Objectives{
@Override @Override
public boolean complete(){ public boolean complete(){
return preset.sector.save != null && preset.sector.save.meta.wave >= preset.captureWave; return preset.sector.save != null && !preset.sector.isAttacked() && preset.sector.hasBase();
} }
@Override @Override

View File

@@ -288,6 +288,10 @@ public class Schematics implements Loadable{
return loadouts.get(block, Seq::new); return loadouts.get(block, Seq::new);
} }
public ObjectMap<CoreBlock, Seq<Schematic>> getLoadouts(){
return loadouts;
}
/** Checks a schematic for deployment validity and adds it to the cache. */ /** Checks a schematic for deployment validity and adds it to the cache. */
private void checkLoadout(Schematic s, boolean validate){ private void checkLoadout(Schematic s, boolean validate){
Stile core = s.tiles.find(t -> t.block instanceof CoreBlock); Stile core = s.tiles.find(t -> t.block instanceof CoreBlock);

View File

@@ -156,7 +156,6 @@ public class SectorInfo{
damage = 0; damage = 0;
if(state.rules.sector != null){ if(state.rules.sector != null){
state.rules.sector.info = this;
state.rules.sector.saveInfo(); state.rules.sector.saveInfo();
} }

View File

@@ -11,6 +11,7 @@ import mindustry.content.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.payloads.*;
import mindustry.world.blocks.storage.CoreBlock.*; import mindustry.world.blocks.storage.CoreBlock.*;
@@ -26,6 +27,8 @@ public class Teams{
public Seq<TeamData> active = new Seq<>(); public Seq<TeamData> active = new Seq<>();
/** Teams with block or unit presence. */ /** Teams with block or unit presence. */
public Seq<TeamData> present = new Seq<>(TeamData.class); public Seq<TeamData> present = new Seq<>(TeamData.class);
/** Ores currently being mined. */
private IntMap<Unit> mined = new IntMap<>();
public Teams(){ public Teams(){
active.add(get(Team.crux)); active.add(get(Team.crux));
@@ -142,7 +145,20 @@ public class Teams{
} }
} }
/** @return whether this ore is taken. */
public boolean canMine(Unit unit, Tile tile){
if(tile == null) return false;
Unit u = mined.get(tile.pos());
return u == unit || u == null;
}
public void registerMined(Tile tile, Unit unit){
if(tile == null || unit == null) return;
mined.put(tile.pos(), unit);
}
public void updateTeamStats(){ public void updateTeamStats(){
mined.clear();
present.clear(); present.clear();
for(Team team : Team.all){ for(Team team : Team.all){

View File

@@ -197,7 +197,7 @@ public class Universe{
if(!sector.isAttacked() && turn > invasionGracePeriod){ if(!sector.isAttacked() && turn > invasionGracePeriod){
//invasion chance depends on # of nearby bases //invasion chance depends on # of nearby bases
if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){ if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 5) * 5; int waveMax = Math.max(sector.info.winWave, state.wave) + Mathf.random(2, 5) * 5;
//assign invasion-related things //assign invasion-related things
if(sector.isBeingPlayed()){ if(sector.isBeingPlayed()){

View File

@@ -5,6 +5,8 @@ import arc.graphics.*;
public class Pal{ public class Pal{
public static Color public static Color
thoriumPink = Color.valueOf("f9a3c7"),
items = Color.valueOf("2ea756"), items = Color.valueOf("2ea756"),
command = Color.valueOf("eab678"), command = Color.valueOf("eab678"),

View File

@@ -279,7 +279,7 @@ public class DesktopInput extends InputHandler{
if(isPlacing() && mode == placing){ if(isPlacing() && mode == placing){
updateLine(selectX, selectY); updateLine(selectX, selectY);
}else if(!selectRequests.isEmpty()){ }else if(!selectRequests.isEmpty() && !ui.chatfrag.shown()){
rotateRequests(selectRequests, Mathf.sign(Core.input.axisTap(Binding.rotate))); rotateRequests(selectRequests, Mathf.sign(Core.input.axisTap(Binding.rotate)));
} }
} }
@@ -660,7 +660,7 @@ public class DesktopInput extends InputHandler{
} }
//update commander unit //update commander unit
if(Core.input.keyTap(Binding.command)){ if(Core.input.keyTap(Binding.command) && unit.type.commandLimit > 0){
Call.unitCommand(player); Call.unitCommand(player);
} }
} }

View File

@@ -336,6 +336,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public static void unitControl(Player player, @Nullable Unit unit){ public static void unitControl(Player player, @Nullable Unit unit){
if(player == null) return; if(player == null) return;
//make sure player is allowed to control the unit
if(net.server() && !netServer.admins.allowAction(player, ActionType.control, action -> action.unit = unit)){
throw new ValidateException(player, "Player cannot control a unit.");
}
//clear player unit when they possess a core //clear player unit when they possess a core
if((unit instanceof BlockUnitc && ((BlockUnitc)unit).tile() instanceof CoreBuild)){ if((unit instanceof BlockUnitc && ((BlockUnitc)unit).tile() instanceof CoreBuild)){
Fx.spawn.at(player); Fx.spawn.at(player);
@@ -376,6 +381,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public static void unitCommand(Player player){ public static void unitCommand(Player player){
if(player == null || player.dead() || !(player.unit() instanceof Commanderc commander)) return; if(player == null || player.dead() || !(player.unit() instanceof Commanderc commander)) return;
//make sure player is allowed to make the command
if(net.server() && !netServer.admins.allowAction(player, ActionType.command, action -> {})){
throw new ValidateException(player, "Player cannot command a unit.");
}
if(commander.isCommanding()){ if(commander.isCommanding()){
commander.clearCommand(); commander.clearCommand();
}else if(player.unit().type.commandLimit > 0){ }else if(player.unit().type.commandLimit > 0){
@@ -935,10 +945,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
boolean canMine(Tile tile){ boolean canMine(Tile tile){
return !Core.scene.hasMouse() return !Core.scene.hasMouse()
&& tile.drop() != null && player.miner().canMine(tile.drop()) && tile.drop() != null
&& player.miner().validMine(tile)
&& !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null) && !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null)
&& player.unit().acceptsItem(tile.drop()) && player.unit().acceptsItem(tile.drop())
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= miningRange; && tile.block() == Blocks.air;
} }
/** Returns the tile at the specified MOUSE coordinates. */ /** Returns the tile at the specified MOUSE coordinates. */
@@ -1203,7 +1214,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
line.x = point.x; line.x = point.x;
line.y = point.y; line.y = point.y;
if(!overrideLineRotation || diagonal){ if(!overrideLineRotation || diagonal){
line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation; if(next != null){
line.rotation = Tile.relativeTo(point.x, point.y, next.x, next.y);
}else if(block.conveyorPlacement && i > 0){
Point2 prev = points.get(i - 1);
line.rotation = Tile.relativeTo(prev.x, prev.y, point.x, point.y);
}else{
line.rotation = baseRotation;
}
}else{ }else{
line.rotation = rotation; line.rotation = rotation;
} }

View File

@@ -613,7 +613,7 @@ public class MobileInput extends InputHandler implements GestureListener{
//reset payload target //reset payload target
payloadTarget = null; payloadTarget = null;
//apply command on double tap when own unit is tapped //apply command on double tap when own unit is tapped
if(Mathf.within(worldx, worldy, player.unit().x, player.unit().y, player.unit().hitSize * 0.6f + 8f)){ if(!player.dead() && Mathf.within(worldx, worldy, player.unit().x, player.unit().y, player.unit().hitSize * 0.6f + 8f) && player.unit().type.commandLimit > 0){
Call.unitCommand(player); Call.unitCommand(player);
}else{ }else{
//control a unit/block //control a unit/block

View File

@@ -72,7 +72,7 @@ public abstract class SaveVersion extends SaveFileReader{
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{ public void writeMeta(DataOutput stream, StringMap tags) throws IOException{
//prepare campaign data for writing //prepare campaign data for writing
if(state.isCampaign()){ if(state.isCampaign()){
state.secinfo.prepare(); state.rules.sector.info.prepare();
state.rules.sector.saveInfo(); state.rules.sector.saveInfo();
} }
@@ -110,11 +110,6 @@ public abstract class SaveVersion extends SaveFileReader{
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
lastReadBuild = map.getInt("build", -1); lastReadBuild = map.getInt("build", -1);
//load in sector info
if(state.rules.sector != null){
state.secinfo = state.rules.sector.info;
}
if(!headless){ if(!headless){
Tmp.v1.tryFromString(map.get("viewpos")); Tmp.v1.tryFromString(map.get("viewpos"));
Core.camera.position.set(Tmp.v1); Core.camera.position.set(Tmp.v1);

View File

@@ -38,12 +38,15 @@ public enum LAccess{
//values with parameters are considered controllable //values with parameters are considered controllable
enabled("to"), //"to" is standard for single parameter access enabled("to"), //"to" is standard for single parameter access
shoot("x", "y", "shoot"), shoot("x", "y", "shoot"),
shootp(true, "unit", "shoot") shootp(true, "unit", "shoot"),
configure(true, 30, "to")
; ;
public final String[] params; public final String[] params;
public final boolean isObj; public final boolean isObj;
/** Tick cooldown between invocations. */
public float cooldown = -1;
public static final LAccess[] public static final LAccess[]
all = values(), all = values(),
@@ -59,4 +62,10 @@ public enum LAccess{
this.params = params; this.params = params;
isObj = obj; isObj = obj;
} }
LAccess(boolean obj, float cooldown, String... params){
this.params = params;
this.cooldown = cooldown;
isObj = obj;
}
} }

View File

@@ -223,6 +223,7 @@ public class LExecutor{
this.outX = outX; this.outX = outX;
this.outY = outY; this.outY = outY;
this.outFound = outFound; this.outFound = outFound;
this.outBuild = outBuild;
} }
public UnitLocateI(){ public UnitLocateI(){
@@ -245,7 +246,7 @@ public class LExecutor{
switch(locate){ switch(locate){
case ore -> { case ore -> {
if(exec.obj(ore) instanceof Item item){ if(exec.obj(ore) instanceof Item item){
res = indexer.findClosestOre(unit.x, unit.y, item); res = indexer.findClosestOre(unit, item);
} }
} }
case building -> { case building -> {
@@ -272,8 +273,9 @@ public class LExecutor{
cache.found = false; cache.found = false;
exec.setnum(outFound, 0); exec.setnum(outFound, 0);
} }
exec.setobj(outFound, res != null && res.build != null && res.build.team == exec.team ? res.build : null); exec.setobj(outBuild, res != null && res.build != null && res.build.team == exec.team ? cache.build = res.build : null);
}else{ }else{
exec.setobj(outBuild, cache.build);
exec.setbool(outFound, cache.found); exec.setbool(outFound, cache.found);
exec.setnum(outX, cache.x); exec.setnum(outX, cache.x);
exec.setnum(outY, cache.y); exec.setnum(outY, cache.y);
@@ -284,20 +286,22 @@ public class LExecutor{
static class Cache{ static class Cache{
float x, y; float x, y;
boolean found; boolean found;
Building build;
} }
} }
/** Controls the unit based on some parameters. */ /** Controls the unit based on some parameters. */
public static class UnitControlI implements LInstruction{ public static class UnitControlI implements LInstruction{
public LUnitControl type = LUnitControl.move; public LUnitControl type = LUnitControl.move;
public int p1, p2, p3, p4; public int p1, p2, p3, p4, p5;
public UnitControlI(LUnitControl type, int p1, int p2, int p3, int p4){ public UnitControlI(LUnitControl type, int p1, int p2, int p3, int p4, int p5){
this.type = type; this.type = type;
this.p1 = p1; this.p1 = p1;
this.p2 = p2; this.p2 = p2;
this.p3 = p3; this.p3 = p3;
this.p4 = p4; this.p4 = p4;
this.p5 = p5;
} }
public UnitControlI(){ public UnitControlI(){
@@ -435,7 +439,7 @@ public class LExecutor{
} }
ai.plan.set(x, y, rot, block); ai.plan.set(x, y, rot, block);
ai.plan.config = null; ai.plan.config = exec.obj(p5) instanceof Content c ? c : null;
builder.clearBuilding(); builder.clearBuilding();
@@ -462,9 +466,8 @@ public class LExecutor{
if(ai.itemTimer > 0) return; if(ai.itemTimer > 0) return;
Building build = exec.building(p1); Building build = exec.building(p1);
int amount = exec.numi(p2); int dropped = Math.min(unit.stack.amount, exec.numi(p2));
int dropped = Math.min(unit.stack.amount, amount); if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange + build.block.size * tilesize/2f)){
if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange)){
int accepted = build.acceptStack(unit.item(), dropped, unit); int accepted = build.acceptStack(unit.item(), dropped, unit);
if(accepted > 0){ if(accepted > 0){
Call.transferItemTo(unit, unit.item(), accepted, unit.x, unit.y, build); Call.transferItemTo(unit, unit.item(), accepted, unit.x, unit.y, build);
@@ -478,7 +481,7 @@ public class LExecutor{
Building build = exec.building(p1); Building build = exec.building(p1);
int amount = exec.numi(p3); int amount = exec.numi(p3);
if(build != null && build.items != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange)){ if(build != null && build.items != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange + build.block.size * tilesize/2f)){
int taken = Math.min(build.items.get(item), Math.min(amount, unit.maxAccepted(item))); int taken = Math.min(build.items.get(item), Math.min(amount, unit.maxAccepted(item)));
if(taken > 0){ if(taken > 0){
@@ -498,6 +501,7 @@ public class LExecutor{
public int target; public int target;
public LAccess type = LAccess.enabled; public LAccess type = LAccess.enabled;
public int p1, p2, p3, p4; public int p1, p2, p3, p4;
public Interval timer = new Interval(1);
public ControlI(LAccess type, int target, int p1, int p2, int p3, int p4){ public ControlI(LAccess type, int target, int p1, int p2, int p3, int p4){
this.type = type; this.type = type;
@@ -513,7 +517,7 @@ public class LExecutor{
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
Object obj = exec.obj(target); Object obj = exec.obj(target);
if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){ if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id) && (type.cooldown <= 0 || timer.get(type.cooldown))){
if(type.isObj){ if(type.isObj){
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4)); b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}else{ }else{
@@ -872,10 +876,11 @@ public class LExecutor{
if(v.isobj && value != 0){ if(v.isobj && value != 0){
String strValue = String strValue =
v.objval == null ? "null" : v.objval == null ? "null" :
v.objval instanceof String ? (String)v.objval : v.objval instanceof String s ? s :
v.objval instanceof MappableContent content ? content.name :
v.objval instanceof Content ? "[content]" : v.objval instanceof Content ? "[content]" :
v.objval instanceof Building ? "[building]" : v.objval instanceof Building build ? build.block.name :
v.objval instanceof Unit ? "[unit]" : v.objval instanceof Unit unit ? unit.type.name :
"[object]"; "[object]";
exec.textBuffer.append(strValue); exec.textBuffer.append(strValue);

View File

@@ -748,7 +748,7 @@ public class LStatements{
@RegisterStatement("ucontrol") @RegisterStatement("ucontrol")
public static class UnitControlStatement extends LStatement{ public static class UnitControlStatement extends LStatement{
public LUnitControl type = LUnitControl.move; public LUnitControl type = LUnitControl.move;
public String p1 = "0", p2 = "0", p3 = "0", p4 = "0"; public String p1 = "0", p2 = "0", p3 = "0", p4 = "0", p5 = "0";
@Override @Override
public void build(Table table){ public void build(Table table){
@@ -777,9 +777,13 @@ public class LStatements{
int c = 0; int c = 0;
for(int i = 0; i < type.params.length; i++){ for(int i = 0; i < type.params.length; i++){
fields(table, type.params[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v).width(110f); fields(table, type.params[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : i == 3 ? p4 : p5, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : i == 3 ? v -> p4 = v : v -> p5 = v).width(100f);
if(++c % 2 == 0) row(table); if(++c % 2 == 0) row(table);
if(i == 3){
table.row();
}
} }
} }
@@ -790,7 +794,7 @@ public class LStatements{
@Override @Override
public LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
return new UnitControlI(type, builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4)); return new UnitControlI(type, builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4), builder.var(p5));
} }
} }
@@ -861,7 +865,7 @@ public class LStatements{
table.table(ts -> { table.table(ts -> {
ts.color.set(table.color); ts.color.set(table.color);
field(ts, ore, str -> ore = str); fields(ts, ore, str -> ore = str);
ts.button(b -> { ts.button(b -> {
b.image(Icon.pencilSmall); b.image(Icon.pencilSmall);
@@ -905,8 +909,10 @@ public class LStatements{
table.add(" found ").left(); table.add(" found ").left();
fields(table, outFound, str -> outFound = str); fields(table, outFound, str -> outFound = str);
if(locate != LLocate.ore){
table.add(" building ").left(); table.add(" building ").left();
fields(table, outBuild, str -> outBuild = str); fields(table, outBuild, str -> outBuild = str);
}
} }

View File

@@ -14,7 +14,7 @@ public enum LUnitControl{
payTake("takeUnits"), payTake("takeUnits"),
mine("x", "y"), mine("x", "y"),
flag("value"), flag("value"),
build("x", "y", "block", "rotation"), build("x", "y", "block", "rotation", "config"),
getBlock("x", "y", "type", "building"), getBlock("x", "y", "type", "building"),
within("x", "y", "radius", "result"); within("x", "y", "radius", "result");

View File

@@ -82,13 +82,13 @@ public class SectorDamage{
/** Applies wave damage based on sector parameters. */ /** Applies wave damage based on sector parameters. */
public static void applyCalculatedDamage(){ public static void applyCalculatedDamage(){
//calculate base damage fraction //calculate base damage fraction
float damage = getDamage(state.secinfo); float damage = getDamage(state.rules.sector.info);
//scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier) //scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier)
float scaled = Mathf.pow(damage, 1.5f); float scaled = Mathf.pow(damage, 1.5f);
//apply damage to units //apply damage to units
float unitDamage = damage * state.secinfo.sumHealth; float unitDamage = damage * state.rules.sector.info.sumHealth;
Tile spawn = spawner.getFirstSpawn(); Tile spawn = spawner.getFirstSpawn();
//damage only units near the spawn point //damage only units near the spawn point
@@ -114,7 +114,7 @@ public class SectorDamage{
} }
} }
if(state.secinfo.wavesPassed > 0){ if(state.rules.sector.info.wavesPassed > 0){
//simply remove each block in the spawner range if a wave passed //simply remove each block in the spawner range if a wave passed
for(Tile spawner : spawner.getSpawns()){ for(Tile spawner : spawner.getSpawns()){
spawner.circle((int)(state.rules.dropZoneRadius / tilesize), tile -> { spawner.circle((int)(state.rules.dropZoneRadius / tilesize), tile -> {
@@ -207,7 +207,7 @@ public class SectorDamage{
//first, calculate the total health of blocks in the path //first, calculate the total health of blocks in the path
//radius around the path that gets counted //radius around the path that gets counted
int radius = 7; int radius = 9;
IntSet counted = new IntSet(); IntSet counted = new IntSet();
for(Tile t : sparse2){ for(Tile t : sparse2){

View File

@@ -37,7 +37,7 @@ public abstract class GenerateFilter{
apply(); apply();
tile.setFloor(in.floor.asFloor()); tile.setFloor(in.floor.asFloor());
tile.setOverlay(!in.floor.asFloor().hasSurface() ? Blocks.air : in.overlay); tile.setOverlay(!in.floor.asFloor().hasSurface() && in.overlay.asFloor().needsSurface ? Blocks.air : in.overlay);
if(!tile.block().synthetic() && !in.block.synthetic()){ if(!tile.block().synthetic() && !in.block.synthetic()){
tile.setBlock(in.block); tile.setBlock(in.block);
@@ -49,7 +49,7 @@ public abstract class GenerateFilter{
apply(); apply();
tile.setFloor(in.floor.asFloor()); tile.setFloor(in.floor.asFloor());
tile.setOverlay(!in.floor.asFloor().hasSurface() ? Blocks.air : in.overlay); tile.setOverlay(!in.floor.asFloor().hasSurface() && in.overlay.asFloor().needsSurface ? Blocks.air : in.overlay);
if(!tile.block().synthetic() && !in.block.synthetic()){ if(!tile.block().synthetic() && !in.block.synthetic()){
tile.setBlock(in.block); tile.setBlock(in.block);

View File

@@ -14,9 +14,11 @@ import static mindustry.Vars.*;
public class FileMapGenerator implements WorldGenerator{ public class FileMapGenerator implements WorldGenerator{
public final Map map; public final Map map;
public final SectorPreset preset;
public FileMapGenerator(String mapName){ public FileMapGenerator(String mapName, SectorPreset preset){
this.map = maps != null ? maps.loadInternalMap(mapName) : null; this.map = maps != null ? maps.loadInternalMap(mapName) : null;
this.preset = preset;
} }
@Override @Override
@@ -56,6 +58,10 @@ public class FileMapGenerator implements WorldGenerator{
if(tile.isCenter() && tile.block() instanceof CoreBlock && tile.team() == state.rules.defaultTeam && !anyCores){ if(tile.isCenter() && tile.block() instanceof CoreBlock && tile.team() == state.rules.defaultTeam && !anyCores){
Schematics.placeLaunchLoadout(tile.x, tile.y); Schematics.placeLaunchLoadout(tile.x, tile.y);
anyCores = true; anyCores = true;
if(preset.addStartingItems){
tile.build.items.add(state.rules.loadout);
}
} }
} }

View File

@@ -569,7 +569,9 @@ public class ContentParser{
if(field.field.getType().isPrimitive()) return; if(field.field.getType().isPrimitive()) return;
if(!field.field.isAnnotationPresent(Nullable.class) && field.field.get(object) == null && !implicitNullable.contains(field.field.getType())){ if(!field.field.isAnnotationPresent(Nullable.class) && field.field.get(object) == null && !implicitNullable.contains(field.field.getType())){
throw new RuntimeException("'" + field.field.getName() + "' in " + object.getClass().getSimpleName() + " is missing!"); throw new RuntimeException("'" + field.field.getName() + "' in " +
((object.getClass().isAnonymousClass() ? object.getClass().getSuperclass() : object.getClass()).getSimpleName()) +
" is missing! Object = " + object + ", field = (" + field.field.getName() + " = " + field.field.get(object) + ")");
} }
}catch(Exception e){ }catch(Exception e){
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@@ -142,11 +142,18 @@ public class Administration{
/** @return whether this action is allowed by the action filters. */ /** @return whether this action is allowed by the action filters. */
public boolean allowAction(Player player, ActionType type, Tile tile, Cons<PlayerAction> setter){ public boolean allowAction(Player player, ActionType type, Tile tile, Cons<PlayerAction> setter){
return allowAction(player, type, action -> setter.get(action.set(player, type, tile)));
}
/** @return whether this action is allowed by the action filters. */
public boolean allowAction(Player player, ActionType type, Cons<PlayerAction> setter){
//some actions are done by the server (null player) and thus are always allowed //some actions are done by the server (null player) and thus are always allowed
if(player == null) return true; if(player == null) return true;
PlayerAction act = Pools.obtain(PlayerAction.class, PlayerAction::new); PlayerAction act = Pools.obtain(PlayerAction.class, PlayerAction::new);
setter.get(act.set(player, type, tile)); act.player = player;
act.type = type;
setter.get(act);
for(ActionFilter filter : actionFilters){ for(ActionFilter filter : actionFilters){
if(!filter.allow(act)){ if(!filter.allow(act)){
Pools.free(act); Pools.free(act);
@@ -699,7 +706,7 @@ public class Administration{
public static class PlayerAction implements Poolable{ public static class PlayerAction implements Poolable{
public Player player; public Player player;
public ActionType type; public ActionType type;
public Tile tile; public @Nullable Tile tile;
/** valid for block placement events only */ /** valid for block placement events only */
public @Nullable Block block; public @Nullable Block block;
@@ -712,6 +719,9 @@ public class Administration{
public @Nullable Item item; public @Nullable Item item;
public int itemAmount; public int itemAmount;
/** valid for unit-type events only, and even in that case may be null. */
public @Nullable Unit unit;
public PlayerAction set(Player player, ActionType type, Tile tile){ public PlayerAction set(Player player, ActionType type, Tile tile){
this.player = player; this.player = player;
this.type = type; this.type = type;
@@ -719,6 +729,13 @@ public class Administration{
return this; return this;
} }
public PlayerAction set(Player player, ActionType type, Unit unit){
this.player = player;
this.type = type;
this.unit = unit;
return this;
}
@Override @Override
public void reset(){ public void reset(){
item = null; item = null;
@@ -728,11 +745,12 @@ public class Administration{
type = null; type = null;
tile = null; tile = null;
block = null; block = null;
unit = null;
} }
} }
public enum ActionType{ public enum ActionType{
breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem, control, command
} }
} }

View File

@@ -17,10 +17,11 @@ public class SectorPreset extends UnlockableContent{
public Cons<Rules> rules = rules -> rules.winWave = captureWave; public Cons<Rules> rules = rules -> rules.winWave = captureWave;
/** Difficulty, 0-10. */ /** Difficulty, 0-10. */
public float difficulty; public float difficulty;
public boolean addStartingItems = false;
public SectorPreset(String name, Planet planet, int sector){ public SectorPreset(String name, Planet planet, int sector){
super(name); super(name);
this.generator = new FileMapGenerator(name); this.generator = new FileMapGenerator(name, this);
this.planet = planet; this.planet = planet;
sector %= planet.sectors.size; sector %= planet.sectors.size;
this.sector = planet.sectors.get(sector); this.sector = planet.sectors.get(sector);

View File

@@ -199,7 +199,7 @@ public class UnitType extends UnlockableContent{
stats.add(Stat.health, health); stats.add(Stat.health, health);
stats.add(Stat.speed, speed); stats.add(Stat.speed, speed);
stats.add(Stat.itemCapacity, health); stats.add(Stat.itemCapacity, itemCapacity);
stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks); stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks);
stats.add(Stat.commandLimit, commandLimit); stats.add(Stat.commandLimit, commandLimit);
//TODO abilities, maybe try something like DPS //TODO abilities, maybe try something like DPS

View File

@@ -50,7 +50,7 @@ public class Weapon{
public float x = 5f, y = 0f; public float x = 5f, y = 0f;
/** random spread on the X axis */ /** random spread on the X axis */
public float xRand = 0f; public float xRand = 0f;
/** radius of occlusion drawn under the weapon; <0 to diable */ /** radius of occlusion drawn under the weapon; <0 to disable */
public float occlusion = -1f; public float occlusion = -1f;
/** fraction of velocity that is random */ /** fraction of velocity that is random */
public float velocityRnd = 0f; public float velocityRnd = 0f;

View File

@@ -74,7 +74,7 @@ public class ParticleWeather extends Weather{
float sspeed = 1f, sscl = 1f, salpha = 1f, offset = 0f; float sspeed = 1f, sscl = 1f, salpha = 1f, offset = 0f;
Color col = Tmp.c1.set(noiseColor); Color col = Tmp.c1.set(noiseColor);
for(int i = 0; i < noiseLayers; i++){ for(int i = 0; i < noiseLayers; i++){
drawNoise(noise, noiseColor, noiseScale * sscl, state.opacity * salpha * opacityMultiplier, baseSpeed * sspeed, state.intensity, windx, windy, offset); drawNoise(noise, noiseColor, noiseScale * sscl, state.opacity * salpha * opacityMultiplier, sspeed * (useWindVector ? 1f : baseSpeed), state.intensity, windx, windy, offset);
sspeed *= noiseLayerSpeedM; sspeed *= noiseLayerSpeedM;
salpha *= noiseLayerAlphaM; salpha *= noiseLayerAlphaM;
sscl *= noiseLayerSclM; sscl *= noiseLayerSclM;

View File

@@ -1,7 +1,6 @@
package mindustry.ui.dialogs; package mindustry.ui.dialogs;
import arc.*; import arc.*;
import arc.input.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.util.*; import arc.util.*;
import mindustry.core.GameState.*; import mindustry.core.GameState.*;
@@ -54,11 +53,7 @@ public class BaseDialog extends Dialog{
} }
public void addCloseListener(){ public void addCloseListener(){
keyDown(key -> { closeOnBack();
if(key == KeyCode.escape || key == KeyCode.back){
Core.app.post(this::hide);
}
});
} }
@Override @Override

View File

@@ -99,7 +99,9 @@ public class LaunchLoadoutDialog extends BaseDialog{
cont.pane(t -> { cont.pane(t -> {
int i = 0; int i = 0;
for(Schematic s : schematics.getLoadouts(core)){ for(var entry : schematics.getLoadouts()){
if(entry.key.size <= core.size){
for(Schematic s : entry.value){
t.button(b -> b.add(new SchematicImage(s)), Styles.togglet, () -> { t.button(b -> b.add(new SchematicImage(s)), Styles.togglet, () -> {
selected = s; selected = s;
@@ -111,6 +113,10 @@ public class LaunchLoadoutDialog extends BaseDialog{
t.row(); t.row();
} }
} }
}
}
}).growX().get().setScrollingDisabled(true, false); }).growX().get().setScrollingDisabled(true, false);
cont.row(); cont.row();

View File

@@ -49,6 +49,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
shouldPause = true; shouldPause = true;
addCloseListener();
buttons.defaults().size(200f, 56f).pad(2); buttons.defaults().size(200f, 56f).pad(2);
buttons.button("@back", Icon.left, this::hide); buttons.button("@back", Icon.left, this::hide);
buttons.button("@techtree", Icon.tree, () -> ui.research.show()); buttons.button("@techtree", Icon.tree, () -> ui.research.show());
@@ -242,7 +244,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
addListener(new ElementGestureListener(){ addListener(new ElementGestureListener(){
@Override @Override
public void tap(InputEvent event, float x, float y, int count, KeyCode button){ public void tap(InputEvent event, float x, float y, int count, KeyCode button){
if(hovered != null && ((mode == look ? canSelect(hovered) && hovered != launchSector : hovered.unlocked()) || debugSelect)){ if(hovered != null && (canSelect(hovered) || debugSelect)){
selected = hovered; selected = hovered;
} }

View File

@@ -281,7 +281,9 @@ public class SettingsMenuDialog extends SettingsDialog{
game.checkPref("blockreplace", true); game.checkPref("blockreplace", true);
game.checkPref("conveyorpathfinding", true); game.checkPref("conveyorpathfinding", true);
game.checkPref("hints", true); game.checkPref("hints", true);
if(!mobile){ if(!mobile){
game.checkPref("backgroundpause", true);
game.checkPref("buildautopause", false); game.checkPref("buildautopause", false);
} }

View File

@@ -368,9 +368,9 @@ public class HudFragment extends Fragment{
c.clearChildren(); c.clearChildren();
for(Item item : content.items()){ for(Item item : content.items()){
if(state.secinfo.getExport(item) >= 1){ if(state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1){
c.image(item.icon(Cicon.small)); c.image(item.icon(Cicon.small));
c.label(() -> (int)state.secinfo.getExport(item) + " /s").color(Color.lightGray); c.label(() -> (int)state.rules.sector.info.getExport(item) + " /s").color(Color.lightGray);
c.row(); c.row();
} }
} }
@@ -379,7 +379,7 @@ public class HudFragment extends Fragment{
c.update(() -> { c.update(() -> {
boolean wrong = false; boolean wrong = false;
for(Item item : content.items()){ for(Item item : content.items()){
boolean has = state.secinfo.getExport(item) >= 1; boolean has = state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1;
if(used.get(item.id) != has){ if(used.get(item.id) != has){
used.set(item.id, has); used.set(item.id, has);
wrong = true; wrong = true;
@@ -389,7 +389,7 @@ public class HudFragment extends Fragment{
rebuild.run(); rebuild.run();
} }
}); });
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.secinfo.getExport(i) > 0)); }).visible(() -> state.isCampaign() && content.items().contains(i -> state.rules.sector != null && state.rules.sector.info.getExport(i) > 0));
}); });
blockfrag.build(parent); blockfrag.build(parent);

View File

@@ -342,7 +342,7 @@ public class PlacementFragment extends Fragment{
topTable.row(); topTable.row();
topTable.table(b -> { topTable.table(b -> {
b.image(Icon.cancel).padRight(2).color(Color.scarlet); b.image(Icon.cancel).padRight(2).color(Color.scarlet);
b.add(!player.isBuilder() ? "@unit.nobuild" : displayBlock.unplaceableMessage()).width(190f).wrap(); b.add(!player.isBuilder() ? "@unit.nobuild" : "@banned").width(190f).wrap();
b.left(); b.left();
}).padTop(2).left(); }).padTop(2).left();
} }

View File

@@ -133,6 +133,8 @@ public class Block extends UnlockableContent{
public int unitCapModifier = 0; public int unitCapModifier = 0;
/** Whether the block can be tapped and selected to configure. */ /** Whether the block can be tapped and selected to configure. */
public boolean configurable; public boolean configurable;
/** If true, this block can be configured by logic. */
public boolean logicConfigurable = false;
/** Whether this block consumes touchDown events when tapped. */ /** Whether this block consumes touchDown events when tapped. */
public boolean consumesTap; public boolean consumesTap;
/** Whether to draw the glow of the liquid for this block, if it has one. */ /** Whether to draw the glow of the liquid for this block, if it has one. */
@@ -189,8 +191,6 @@ public class Block extends UnlockableContent{
public float buildCost; public float buildCost;
/** Whether this block is visible and can currently be built. */ /** Whether this block is visible and can currently be built. */
public BuildVisibility buildVisibility = BuildVisibility.hidden; public BuildVisibility buildVisibility = BuildVisibility.hidden;
/** Defines when this block can be placed. */
public BuildPlaceability buildPlaceability = BuildPlaceability.always;
/** Multiplier for speed of building this block. */ /** Multiplier for speed of building this block. */
public float buildCostMultiplier = 1f; public float buildCostMultiplier = 1f;
/** Multiplier for cost of research in tech tree. */ /** Multiplier for cost of research in tech tree. */
@@ -519,7 +519,7 @@ public class Block extends UnlockableContent{
} }
public boolean isPlaceable(){ public boolean isPlaceable(){
return isVisible() && buildPlaceability.placeable() && !state.rules.bannedBlocks.contains(this); return isVisible() && !state.rules.bannedBlocks.contains(this);
} }
/** Called when building of this block begins. */ /** Called when building of this block begins. */
@@ -532,11 +532,6 @@ public class Block extends UnlockableContent{
} }
/** @return a message detailing why this block can't be placed. */
public String unplaceableMessage(){
return state.rules.bannedBlocks.contains(this) ? Core.bundle.get("banned") : buildPlaceability.message();
}
public boolean isFloor(){ public boolean isFloor(){
return this instanceof Floor; return this instanceof Floor;
} }
@@ -622,7 +617,7 @@ public class Block extends UnlockableContent{
public ItemStack[] researchRequirements(){ public ItemStack[] researchRequirements(){
ItemStack[] out = new ItemStack[requirements.length]; ItemStack[] out = new ItemStack[requirements.length];
for(int i = 0; i < out.length; i++){ for(int i = 0; i < out.length; i++){
int quantity = 40 + Mathf.round(Mathf.pow(requirements[i].amount, 1.15f) * 20 * researchCostMultiplier, 10); int quantity = 60 + Mathf.round(Mathf.pow(requirements[i].amount, 1.15f) * 20 * researchCostMultiplier, 10);
out[i] = new ItemStack(requirements[i].item, UI.roundAmount(quantity)); out[i] = new ItemStack(requirements[i].item, UI.roundAmount(quantity));
} }
@@ -674,6 +669,14 @@ public class Block extends UnlockableContent{
consumes.init(); consumes.init();
if(!logicConfigurable){
configurations.each((key, val) -> {
if(UnlockableContent.class.isAssignableFrom(key)){
logicConfigurable = true;
}
});
}
if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){ if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){
throw new IllegalArgumentException("Consumer using buffered power: " + name); throw new IllegalArgumentException("Consumer using buffered power: " + name);
} }

View File

@@ -126,7 +126,6 @@ public class Build{
for(int dy = 0; dy < type.size; dy++){ for(int dy = 0; dy < type.size; dy++){
int wx = dx + offsetx + tile.x, wy = dy + offsety + tile.y; int wx = dx + offsetx + tile.x, wy = dy + offsety + tile.y;
Tile check = world.tile(wx, wy); Tile check = world.tile(wx, wy);
if( if(
@@ -136,6 +135,10 @@ public class Build{
!check.interactable(team) || //cannot interact !check.interactable(team) || //cannot interact
!check.floor().placeableOn || //solid wall !check.floor().placeableOn || //solid wall
!((type.canReplace(check.block()) || //can replace type !((type.canReplace(check.block()) || //can replace type
//controversial change: allow rebuilding damaged blocks
//this could be buggy and abusable, so I'm not enabling it yet
//note that this requires a change in BuilderComp as well
//(type == check.block() && check.centerX() == x && check.centerY() == y && check.build != null && check.build.health < check.build.maxHealth - 0.0001f) ||
(check.block instanceof ConstructBlock && check.<ConstructBuild>bc().cblock == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction (check.block instanceof ConstructBlock && check.<ConstructBuild>bc().cblock == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
type.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(check.block.bounds(check.centerX(), check.centerY(), Tmp.r2))) || //no replacement type.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(check.block.bounds(check.centerX(), check.centerY(), Tmp.r2))) || //no replacement
(type.requiresWater && check.floor().liquidDrop != Liquids.water) //requires water but none found (type.requiresWater && check.floor().liquidDrop != Liquids.water) //requires water but none found

View File

@@ -119,7 +119,7 @@ public class LaunchPad extends Block{
table.row(); table.row();
table.label(() -> { table.label(() -> {
Sector dest = state.secinfo.getRealDestination(); Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
return Core.bundle.format("launch.destination", return Core.bundle.format("launch.destination",
dest == null ? Core.bundle.get("sectors.nonelaunch") : dest == null ? Core.bundle.get("sectors.nonelaunch") :
@@ -135,7 +135,11 @@ public class LaunchPad extends Block{
} }
table.button(Icon.upOpen, Styles.clearTransi, () -> { table.button(Icon.upOpen, Styles.clearTransi, () -> {
ui.planet.showSelect(state.rules.sector, other -> state.secinfo.destination = other); ui.planet.showSelect(state.rules.sector, other -> {
if(state.isCampaign()){
state.rules.sector.info.destination = other;
}
});
deselect(); deselect();
}).size(40f); }).size(40f);
} }
@@ -208,7 +212,7 @@ public class LaunchPad extends Block{
public void remove(){ public void remove(){
if(!state.isCampaign()) return; if(!state.isCampaign()) return;
Sector destsec = state.secinfo.getRealDestination(); Sector destsec = state.rules.sector.info.getRealDestination();
//actually launch the items upon removal //actually launch the items upon removal
if(team() == state.rules.defaultTeam){ if(team() == state.rules.defaultTeam){
@@ -219,7 +223,7 @@ public class LaunchPad extends Block{
dest.add(stack); dest.add(stack);
//update export //update export
state.secinfo.handleItemExport(stack); state.rules.sector.info.handleItemExport(stack);
Events.fire(new LaunchItemEvent(stack)); Events.fire(new LaunchItemEvent(stack));
} }

View File

@@ -183,7 +183,7 @@ public class PowerNode extends PowerBlock{
protected void getPotentialLinks(Tile tile, Cons<Building> others){ protected void getPotentialLinks(Tile tile, Cons<Building> others){
Boolf<Building> valid = other -> other != null && other.tile() != tile && other.power != null && Boolf<Building> valid = other -> other != null && other.tile() != tile && other.power != null &&
((!other.block.outputsPower && other.block.consumesPower) || (other.block.outputsPower && !other.block.consumesPower) || other.block instanceof PowerNode) && (other.block.outputsPower || other.block.consumesPower || other.block instanceof PowerNode) &&
overlaps(tile.x * tilesize + offset, tile.y * tilesize + offset, other.tile(), laserRange * tilesize) && other.team == player.team() overlaps(tile.x * tilesize + offset, tile.y * tilesize + offset, other.tile(), laserRange * tilesize) && other.team == player.team()
&& !other.proximity.contains(e -> e.tile == tile) && !graphs.contains(other.power.graph); && !other.proximity.contains(e -> e.tile == tile) && !graphs.contains(other.power.graph);

View File

@@ -37,11 +37,6 @@ public class GenericCrafter extends Block{
@Override @Override
public void setStats(){ public void setStats(){
if(consumes.has(ConsumeType.liquid)){
ConsumeLiquidBase cons = consumes.get(ConsumeType.liquid);
cons.timePeriod = craftTime;
}
super.setStats(); super.setStats();
stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds); stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds);
@@ -50,7 +45,7 @@ public class GenericCrafter extends Block{
} }
if(outputLiquid != null){ if(outputLiquid != null){
stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount, false); stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount * (60f / craftTime), true);
} }
} }

View File

@@ -27,7 +27,7 @@ public class LiquidConverter extends GenericCrafter{
public void setStats(){ public void setStats(){
super.setStats(); super.setStats();
stats.remove(Stat.output); stats.remove(Stat.output);
stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount * craftTime, false); stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount * 60f, true);
} }
public class LiquidConverterBuild extends GenericCrafterBuild{ public class LiquidConverterBuild extends GenericCrafterBuild{

View File

@@ -34,11 +34,6 @@ public class Separator extends Block{
@Override @Override
public void setStats(){ public void setStats(){
if(consumes.has(ConsumeType.liquid)){
ConsumeLiquidBase cons = consumes.get(ConsumeType.liquid);
cons.timePeriod = craftTime;
}
super.setStats(); super.setStats();
stats.add(Stat.output, new ItemFilterValue(item -> { stats.add(Stat.output, new ItemFilterValue(item -> {

View File

@@ -339,15 +339,15 @@ public class CoreBlock extends StorageBlock{
public void itemTaken(Item item){ public void itemTaken(Item item){
if(state.isCampaign() && team == state.rules.defaultTeam){ if(state.isCampaign() && team == state.rules.defaultTeam){
//update item taken amount //update item taken amount
state.secinfo.handleCoreItem(item, -1); state.rules.sector.info.handleCoreItem(item, -1);
} }
} }
@Override @Override
public void handleItem(Building source, Item item){ public void handleItem(Building source, Item item){
if(net.server() || !net.active()){ if(net.server() || !net.active()){
if(team == state.rules.defaultTeam){ if(team == state.rules.defaultTeam && state.isCampaign()){
state.secinfo.handleCoreItem(item, 1); state.rules.sector.info.handleCoreItem(item, 1);
} }
if(items.get(item) >= getMaximumAccepted(item)){ if(items.get(item) >= getMaximumAccepted(item)){

View File

@@ -33,8 +33,6 @@ public class UnitFactory extends UnitBlock{
hasPower = true; hasPower = true;
hasItems = true; hasItems = true;
solid = true; solid = true;
//flags = EnumSet.of(BlockFlag.producer, BlockFlag.unitModifier);
//unitCapModifier = 2;
configurable = true; configurable = true;
outputsPayload = true; outputsPayload = true;
rotate = true; rotate = true;
@@ -44,6 +42,11 @@ public class UnitFactory extends UnitBlock{
tile.progress = 0; tile.progress = 0;
}); });
config(UnitType.class, (UnitFactoryBuild tile, UnitType val) -> {
tile.currentPlan = plans.indexOf(p -> p.unit == val);
tile.progress = 0;
});
consumes.add(new ConsumeItemDynamic((UnitFactoryBuild e) -> e.currentPlan != -1 ? plans.get(e.currentPlan).requirements : ItemStack.empty)); consumes.add(new ConsumeItemDynamic((UnitFactoryBuild e) -> e.currentPlan != -1 ? plans.get(e.currentPlan).requirements : ItemStack.empty));
} }
@@ -88,6 +91,18 @@ public class UnitFactory extends UnitBlock{
super.setStats(); super.setStats();
stats.remove(Stat.itemCapacity); stats.remove(Stat.itemCapacity);
stats.add(Stat.output, table -> {
Seq<UnitPlan> p = plans.select(u -> u.unit.unlockedNow());
table.row();
for(var plan : p){
if(plan.unit.unlockedNow()){
table.image(plan.unit.icon(Cicon.small)).size(8 * 3).padRight(2).right();
table.add(plan.unit.localizedName).left();
table.row();
}
}
});
} }
@Override @Override

View File

@@ -46,6 +46,6 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
@Override @Override
public void display(Stats stats){ public void display(Stats stats){
stats.add(booster ? Stat.booster : Stat.input, liquid, amount * timePeriod, timePeriod == 60); stats.add(booster ? Stat.booster : Stat.input, liquid, amount * 60f, true);
} }
} }

View File

@@ -5,13 +5,6 @@ import mindustry.gen.*;
public abstract class ConsumeLiquidBase extends Consume{ public abstract class ConsumeLiquidBase extends Consume{
/** amount used per frame */ /** amount used per frame */
public final float amount; public final float amount;
/**
* How much time is taken to use this liquid, in ticks. Used only for visual purposes.
* Example: a normal ConsumeLiquid with 10/s and a 10 second timePeriod would display as "100 seconds".
* Without a time override, it would display as "10 liquid/second".
* This is used for generic crafters.
*/
public float timePeriod = 60;
public ConsumeLiquidBase(float amount){ public ConsumeLiquidBase(float amount){
this.amount = amount; this.amount = amount;

View File

@@ -50,6 +50,6 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
@Override @Override
public void display(Stats stats){ public void display(Stats stats){
stats.add(booster ? Stat.booster : Stat.input, new LiquidFilterValue(filter, amount * timePeriod, timePeriod == 60f)); stats.add(booster ? Stat.booster : Stat.input, new LiquidFilterValue(filter, amount * 60f, true));
} }
} }

View File

@@ -1,32 +0,0 @@
package mindustry.world.meta;
import arc.*;
import arc.func.*;
import mindustry.*;
import java.util.*;
/**
* Like BuildVisiblity, but defines whether a block can be *placed*, with an extra message.
* This is like defining a conditionally banned block.
* */
public enum BuildPlaceability{
always(() -> true),
sectorCaptured(() -> Vars.state.rules.sector != null && Vars.state.rules.sector.isCaptured());
private final Boolp placeability;
BuildPlaceability(Boolp placeability){
this.placeability = placeability;
}
public boolean placeable(){
return placeability.get();
}
/** @return why this block is banned. */
public String message(){
return Core.bundle.get("unplaceable." + name().toLowerCase(Locale.ROOT));
}
}

View File

@@ -9,7 +9,7 @@ public enum BuildVisibility{
debugOnly(() -> false), debugOnly(() -> false),
sandboxOnly(() -> Vars.state.rules.infiniteResources), sandboxOnly(() -> Vars.state.rules.infiniteResources),
campaignOnly(() -> Vars.state.isCampaign()), campaignOnly(() -> Vars.state.isCampaign()),
lightingOnly(() -> Vars.state.rules.lighting), lightingOnly(() -> Vars.state.rules.lighting || Vars.state.isCampaign()),
ammoOnly(() -> Vars.state.rules.unitAmmo); ammoOnly(() -> Vars.state.rules.unitAmmo);
private final Boolp visible; private final Boolp visible;

View File

@@ -0,0 +1,12 @@
- Added 4 new custom game maps (untested)
- Added new stats to unit info
- Added buttons to clear campaign research+saves
- Added fog weather
- Added weather to some custom game maps
- Added support for weather in JSON mods
- Added better control sync for commanded units
- Added recipe display for reconstructors
- Added titanium fuse ammo
- Made campaign load research from older versions (5.0)
- Campaign: Added more schematics for bases to use
- router.

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=f8ef0f3a72de0f79ca49da895fed0b6889cd597e archash=7090a07ce5d02dcb697f6c5a8d1fb6e0a00de45f