diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a2145fbe4c..f8a3964987 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -33,7 +33,7 @@ jobs: - name: Update bundles if: ${{ github.repository == 'Anuken/Mindustry' }} run: | - ./gradlew updateBundles + ./gradlew -DcompileMethodParams updateBundles if [ -n "$(git status --porcelain)" ]; then git config --global user.name "Github Actions" @@ -45,7 +45,7 @@ jobs: - name: Update scripts, class mappings and map names if: ${{ github.repository == 'Anuken/Mindustry' }} run: | - ./gradlew updateScripts fixMaps + ./gradlew -DcompileMethodParams updateScripts fixMaps if [ -n "$(git status --porcelain)" ]; then git config --global user.name "Github Actions" diff --git a/build.gradle b/build.gradle index c43ee7b6cd..ad9b9e26ae 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ allprojects{ if(!project.hasProperty("versionType")) versionType = 'official' appName = 'Mindustry' steamworksVersion = 'da41c05b7a2a44c8625ca0ba7d6ca1a2e73f621a' - rhinoVersion = 'e74ac129d2bff72c1deb536d7d99a0dbb7046d2e' + rhinoVersion = '32395f942976a6d1d21ec9877664554cd5762306' loadVersionProps = { return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p } @@ -529,3 +529,9 @@ task resolveDependencies{ } } } + +if(System.getProperty("compileMethodParams") != null){ + project(":core").tasks.withType(JavaCompile).configureEach{ + options.compilerArgs << '-parameters' + } +} \ No newline at end of file diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index d8d0891ad9..e9d7faeb4d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -321,8 +321,8 @@ server.admins.none = No admins found! server.add = Add Server server.delete = Are you sure you want to delete this server? server.edit = Edit Server -server.outdated = [scarlet]Outdated Server![] -server.outdated.client = [scarlet]Outdated Client![] +server.outdated = [red]⚠ Outdated Server![] +server.outdated.client = [red]⚠ Outdated Client![] server.version = [gray]v{0} {1} server.custombuild = [accent]Custom Build confirmban = Are you sure you want to ban "{0}[white]"? diff --git a/core/assets/bundles/bundle_bg.properties b/core/assets/bundles/bundle_bg.properties index 5e96f52b60..4c01408c35 100644 --- a/core/assets/bundles/bundle_bg.properties +++ b/core/assets/bundles/bundle_bg.properties @@ -319,8 +319,8 @@ server.admins.none = Няма намерени администратори! server.add = Добави Сървър server.delete = Сигурни ли сте, че искате да изтриете този сървър? server.edit = Редактирай Сървър -server.outdated = [scarlet]Остарял Сървър![] -server.outdated.client = [scarlet]Остарял Клиент![] +server.outdated = [red]⚠ Остарял Сървър![] +server.outdated.client = [red]⚠ Остарял Клиент![] server.version = [gray]в{0} {1} server.custombuild = [accent]Персонализирана компилация confirmban = Сигурни ли сте, че искате да баннете "{0}[white]"? diff --git a/core/assets/bundles/bundle_ca.properties b/core/assets/bundles/bundle_ca.properties index 5324f754c9..fa3420152a 100644 --- a/core/assets/bundles/bundle_ca.properties +++ b/core/assets/bundles/bundle_ca.properties @@ -1,4 +1,4 @@ -credits.text = Creat per [royal]Anuken[] - [sky]anukendev@gmail.com[] +credits.text = Created by [royal]Anuken[] - [sky]anukendev@gmail.com[] credits = Crèdits contributors = Traductors i col·laboradors discord = Uniu-vos al Discord del Mindustry! @@ -15,11 +15,11 @@ link.wiki.description = Viquipèdia oficial del Mindustry link.suggestions.description = Suggereix canvis link.bug.description = Heu trobat un error. Informeu-ne aquí! linkopen = Aquest servidor us ha enviat un enllaç. Esteu segur que el voleu obrir?\n\n[sky]{0} -clipboardcopy = This server wants to copy text to your clipboard. Are you sure you want to continue?\n\n[sky]{0} +clipboardcopy = Aquest servidor vol copiar text al vostre porta-retalls. Esteu segur que voleu continuar?\n\n[sky]{0} linkfail = No s’ha pogut obrir l’enllaç!\nLa direcció URL s’ha copiat al porta-retalls. screenshot = S’ha desat la captura de pantalla a {0}. screenshot.invalid = El mapa és massa gran. No hi ha prou memòria per a fer la captura de pantalla. -screenshot.error = Failed to take screenshot. Your device likely does not support screenshots this large. +screenshot.error = No s’ha pogut fer la captura de pantalla. El vostre dispositiu probablement no suporta captures tan grans. gameover = Fi de la partida gameover.disconnect = Desconnecta gameover.pvp = Ha guanyat l’equip[accent] {0}[]! @@ -27,7 +27,7 @@ gameover.waiting = [accent]S’espera el proper mapa… highscore = [accent]Nova puntuació rècord! copied = S’ha copiat. indev.notready = Aquesta part del joc encara no està preparada. -bundle.external = Note: You have successfully loaded an external translation bundle.\n[accent]{0} +bundle.external = Avís: S’ha carregat correctament un paquet de traduccions externes.\n[accent]{0} load.sound = Sons load.map = Mapes @@ -52,7 +52,7 @@ mods.browser.view-releases = Veure versions publicades mods.browser.noreleases = [scarlet]No s’han trobat versions publicades.\n[accent]No s’ha trobat cap versió publicada d’aquest mod. Comproveu si el dipòsit del mod n’ha publicat alguna. mods.browser.latest = [lightgray][La versió més recent] mods.browser.releases = Versions -mods.browser.fetching = Fetching Releases... +mods.browser.fetching = Es descarreguen les versions publicades... mods.github.open = Dipòsit de GitHub mods.github.open-release = Pàgina de versions publicades mods.browser.sortdate = Ordena per data @@ -62,7 +62,7 @@ export.file = Exporta un fitxer import.file = Importa un fitxer copy.clipboard = Copia al porta-retalls load.clipboard = Importa del porta-retalls -workshop.browse = Browse Workshop +workshop.browse = Cerca al Workshop workshop.share = Comparteix al Workshop de l’Steam schematic = Esquema @@ -106,8 +106,8 @@ coreattack = < Ataquen el nucli! > nearpoint = [[ [scarlet]ABANDONEU EL PUNT D’ATERRATGE IMMEDIATAMENT[] ]\nAniquilació imminent database = Base de dades del nucli database.button = Base de dades -database.patched = Modified by data patches. -viewfields = View Content Fields +database.patched = Modificat pels pedaços de dades. +viewfields = Mostra els camps de contingut savegame = Desa la partida loadgame = Carrega una partida joingame = Uneix-me a una partida @@ -134,7 +134,7 @@ export = Export preparingconfig = Es prepara la configuració… preparingcontent = Es prepara el contingut… uploadingcontent = Es puja el contingut… -content.createdby = Created by: {0} +content.createdby = Autoria: {0} uploadingpreviewfile = Es puja el fitxer de previsualització… committingchanges = S’apliquen els canvis… done = Fet! @@ -150,14 +150,14 @@ mods.openfolder = Obre la carpeta mods.viewcontent = Mostra el contingut mods.reload = Torna a carregar mods.reloadexit = El programa es tancarà per a carregar els mods. -mods.update.available = [lightgray]Update Available: {0} -mods.update.all = Update All Mods -mods.downloading = [accent]Downloading Mod: {0} +mods.update.available = [lightgray]Actualització disponible: {0} +mods.update.all = Actualitza tots els mods +mods.downloading = [accent]Es descarrega el mod {0} -mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. -mod.warning.read = I actually read this -mod.warning.accept = I accept the responsibility -mod.warning.deny = Disable my mods +mod.warning = ⚠ Els mods poden executar codi arbitrari.\n\nNo descarregueu mai mods d’orígens amb els quals no hi confieu. Podrien robar-vos dades o comprometre el vostre ordinador. +mod.warning.read = Ho he llegit +mod.warning.accept = N’accepto la responsabilitat +mod.warning.deny = Desactiva tots els mods mod.installed = [[Instal·lat] mod.display = [gray]Mod:[orange] {0} mod.enabled = [lightgray]Activat @@ -187,7 +187,7 @@ mod.incompletedependencies.details = Aquest mod no es pot carregar perquè té u mod.requiresversion = Cal la versió [red]{0}[] del joc. mod.errors = S’han produït errors quan es carregava el contingut. -mod.errors.map = [scarlet]Errors have occurred loading map content. All custom content has been disabled.[]\nThis map will likely not function correctly. +mod.errors.map = [scarlet]S’han produït errors mentre es carregaven els continguts del mapa. Tot el contingut personalitzat s’ha desactivat.[]\nProbablement aquest mapa no funcionarà correctament. mod.noerrorplay = [scarlet]S’executen mods amb errors.[] Desactiveu els mods afectats o arregleu les errades abans de jugar. mod.enable = Activa mod.requiresrestart = El programa es tancarà per a aplicar els canvis. @@ -198,7 +198,7 @@ mod.import.github = Importa des de GitHub mod.jarwarn = [scarlet]Els mods JAR no són segurs.[]\nAssegureu-vos que l’heu importat d’una font fiable. mod.item.remove = Aquest element és part del mod[accent] «{0}»[]. Per a treure’l, cal desinstal·lar el mod. mod.remove.confirm = Aquest mod s’esborrarà. -mod.author = [lightgray]Autor:[] {0} +mod.author = [lightgray]Autoria:[] {0} mod.missing = Aquesta partida desada conté mods que heu actualitzat fa poc o que ja no teniu instal·lats. La partida desada es pot corrompre. Esteu segur que voleu carregar-la?\n[lightgray]Mods:\n{0} mod.preview.missing = Abans de publicar aquest mod al Workshop, heu d’afegir una imatge de previsualització.\nPoseu una imatge amb el nom [accent]«preview.png»[] dins la carpeta del mod i proveu-ho de nou. mod.folder.missing = Al Workshop només es poden publicar els mods organitzats degudament en una carpeta.\nPer convertir qualsevol mod en format *.zip, descomprimiu els continguts en una carpeta i esborreu el fitxer comprimit; després, reinicieu el programa o recarregueu els mods. @@ -229,8 +229,8 @@ campaign.none = [lightgray]Trieu en quin planeta voleu començar.\nEs pot canvia campaign.erekir = [accent]Recomanat per a jugadors novells.[]\n\nContingut revisat nou. Una campanya de progressió més o menys lineal.\n\nMapes de qualitat més alta i experiència més satisfactòria. campaign.serpulo = [scarlet]No recomanat per a jugadors novells.[]\n\nContingut antic: l’experiència clàssica. Campanya més oberta.\n\nPotser els mapes i mecàniques de la campanya no estan massa equilibrats. Contingut en general menys polit que el d’Erekir. campaign.difficulty = Dificultat -campaign.rework.title = Campaign Changes Notice -campaign.rework.serpulo = [accent]⚠ The Serpulo campaign has been reworked.[]\n\nSector positions and difficulty have changed. You may encounter issues relating to leftover campaign saves from the old layout.\n\nFor a cleaner experience, consider resetting your campaign saves in the settings. +campaign.rework.title = Avís de canvis de campanya +campaign.rework.serpulo = [accent]⚠ La campanya de Serpulo s’ha reestructurat.[]\n\nLes posicions dels sectors i la dificultat s’han modificat. Potser tindreu problemes amb les desades antigues de la campanya amb la disposició antiga.\n\nPer a una experiència sense tants problemes, considereu reiniciar la campanya des de la configuració del joc. completed = [accent]Completat techtree = Arbre tecnològic techtree.select = Selecció de l’arbre tecnològic @@ -254,7 +254,7 @@ server.kicked.vote = Us han expulsat per votació. Adéu. server.kicked.clientOutdated = El client s’ha d’actualitzar. server.kicked.serverOutdated = El servidor s’ha d’actualitzar. Demaneu-li a l’amfitrió que l’actualitzi. server.kicked.banned = Se us ha bandejat d’aquest servidor. -server.kicked.steamBanned = You have been banned from Steam lobbies for disruptive and shameful behavior. +server.kicked.steamBanned = Se us ha bandejat de les sales de l’Steam per comportament inadequat. server.kicked.typeMismatch = El servidor no és compatible amb el tipus de compilació actual. server.kicked.playerLimit = El servidor està ple. Espereu que hi hagi una ranura buida. server.kicked.recentKick = Us han expulsat fa poc.\nEspereu abans de connectar-vos. @@ -285,12 +285,12 @@ servers.remote = Servidors remots servers.global = Servidors de la comunitat servers.disclaimer = Els servidors de la comunitat [accent]no[] són ni els controla el desenvolupador.\n\nEls servidors poden tenir continguts generats per usuaris que no són adequats per a totes les edats. -servers.connect.confirm = Join the following server?\n\n[accent]{0}[]\n\nOnly connect to servers you trust! +servers.connect.confirm = Voleu unir-vos al servidor següent?\n\n[accent]{0}[]\n\nConnecteu-vos només a servidors de confiança! servers.showhidden = Mostra els servidors ocults server.shown = Mostra server.hidden = Amaga -servers.sortping = Sorting By: Ping -servers.sortplayers = Sorting By: Player Count +servers.sortping = Ordena per: Ping +servers.sortplayers = Ordena per: Nombre de jugadors viewplayer = Es mira el jugador: [accent]{0} trace = Rastreja un jugador @@ -319,8 +319,8 @@ server.admins.none = No s’ha trobat cap administrador! server.add = Afegeix un servidor server.delete = Esteu segur que voleu esborrar aquest servidor? server.edit = Edita el servidor -server.outdated = [scarlet]Servidor desactualitzat![] -server.outdated.client = [scarlet]Client desactualitzat![] +server.outdated = [red]⚠ Servidor desactualitzat![] +server.outdated.client = [red]⚠ Client desactualitzat![] server.version = [gray]v{0} {1} server.custombuild = [accent]Versió personalitzada confirmban = Esteu segur que voleu bandejar a «{0}[white]»? @@ -339,14 +339,14 @@ disconnect.timeout = S’ha esgotat el temps d’espera. disconnect.data = No s’han pogut carregar les dades del món! disconnect.snapshottimeout = S’ha esgotat el temps d’espera mentre es rebien els UDP instantanis.\nPot ser degut a una connexió o xarxa inestables. cantconnect = No és possible unir-se a la partida ([accent]{0}[]). -disconnect.reason = Disconnected: {0} +disconnect.reason = Desconnectat: {0} connecting = [accent]Es connecta… reconnecting = [accent]Es torna a connectar… -connecting.establish = [accent]Establishing connection... -receiving.world = [accent]Receiving world data ({0})... -receiving.assets = [accent]Receiving assets ({0})... -receiving.assets.fail = Error occurred while receiving assets. +connecting.establish = [accent]S’estableix la connexió… +receiving.world = [accent]Es reben les dades del món ({0})… +receiving.assets = [accent]Es reben recursos ({0})… +receiving.assets.fail = S’han produït errors mentre es rebien recursos. server.port = Port: server.invalidport = El número de port no és vàlid! @@ -364,10 +364,10 @@ save.export = Exporta la desada save.import.invalid = [accent]Aquesta desada no és vàlida! save.import.fail = [scarlet]La desada no s’ha pogut importar: [accent]{0} save.export.fail = [scarlet]La desada no s’ha pogut exportar: [accent]{0} -save.export.needsload = [scarlet]⚠ This save contains embedded assets.[]\nLoad into this save before exporting it. -save.assets.missing = Missing Assets -save.assets.missing.list = Asset List... -save.assets.missing.info = [scarlet]⚠ This save contains missing assets![]\n\n[lightgray]This is likely because the save file was not properly exported.\n[lightgray]When sharing saves, only export through the in-game UI. Do not copy save files directly.\n[lightgray]If transferring files is necessary, make sure the [accent]assetCache[] folder is copied as well. +save.export.needsload = [scarlet]⚠ Aquesta desada conté recursos incrustats.[]\nCarregueu la desada abans d’exportar-la. +save.assets.missing = Recursos que falten +save.assets.missing.list = Llista de recursos… +save.assets.missing.info = [scarlet]⚠ Aquesta desada fa servir recursos que fan falta![]\n\n[lightgray]Probablement, la desada no es va exportar adequadament.\n[lightgray]Quan es comparteixen partides desades, exporteu-les només a través de la interfície del joc. No copieu els fitxers de desades directament.\n[lightgray]Si cal transferir fitxers, assegureu-vos que la carpeta [accent]assetCache[] també es copia. save.import = Importa una partida desada save.newslot = Nom del fitxer de la desada: save.rename = Reanomena @@ -392,7 +392,7 @@ confirm = Confirmació delete = Esborra view.workshop = Mostra-ho al Workshop workshop.listing = Edita el llistat del Workshop -hide = Hide +hide = Amaga ok = D’acord open = Obre customize = Personalitza les regles @@ -414,9 +414,9 @@ stance.shoot = Comportament: Dispara stance.holdfire = Comportament: Mantén el foc stance.pursuetarget = Comportament: Persegueix l’objectiu stance.patrol = Comportament: Patrulla el camí -stance.holdposition = Stance: Hold Position +stance.holdposition = Comportament: Manté la posició stance.ram = Comportament: Senzill\n[lightgray]Mou-te en línia recta, sense encaminador -stance.boost = Boost +stance.boost = Sobrevola stance.mineauto = Extracció automàtica stance.mine = Extrau l’element: {0} openlink = Obre l’enllaç @@ -463,7 +463,7 @@ saveimage = Desa la imatge unknown = Desconegut custom = Personalitzat builtin = *Integrat* -modded = Modded +modded = Amb mods map.delete.confirm = Esteu segur que voleu esborrar aquest mapa? Aquesta acció no es pot desfer! map.random = [accent]Mapa aleatori map.nospawn = Aquest mapa no té cap nucli per tal que el jugador hi pugui aparèixer! Afegiu-hi un nucli {0} amb l’editor. @@ -495,7 +495,7 @@ editor.openin = Obre a l’editor editor.oregen = Generació de minerals editor.oregen.info = Generació de minerals: editor.mapinfo = Informació del mapa -editor.author = Autor: +editor.author = Autoria: editor.description = Descripció: editor.nodescription = Un mapa ha de tenir una descripció amb almenys 4 caràcters per tal que es pugui publicar. editor.waves = Onades @@ -521,83 +521,83 @@ editor.filters.type = Tipus de mapa: editor.filters.search = Cerca a: editor.filters.author = Autor editor.filters.description = Descripció -editor.filters.modname = Mod Name -editor.filters.prioritizemod = Mod Priority -editor.filters.prioritizecustom = Custom Priority -editor.filters.planetselect = Planet Selection +editor.filters.modname = Nom del mod +editor.filters.prioritizemod = Prioritat del mod +editor.filters.prioritizecustom = Prioritat personalitzada +editor.filters.planetselect = Selecció de planeta editor.shiftx = Desplaça en l’eix X editor.shifty = Desplaça en l’eix Y asset.emoji = Emojis -asset.patch = Patch -asset.content = Content -asset.bundle = Translation Bundle -asset.image = Image -asset.sound = Sound -asset.music = Music +asset.patch = Pedaç +asset.content = Contingut +asset.bundle = Paquet de traduccions +asset.image = Imatge +asset.sound = So +asset.music = Música -content.unit = Unit -content.item = Item -content.liquid = Liquid -content.block = Block -content.planet = Planet -content.weather = Weather -content.status = Status Effect +content.unit = Unitat +content.item = Element +content.liquid = Líquid +content.block = Bloc +content.planet = Planeta +content.weather = Clima +content.status = Efecte d’estat -patch.set = Patchset: {0} +patch.set = Conjunt de pedaços: {0} patch.none = [lightgray]No s’han carregat pedaços. patch.errors = Errors del conjunt de pedaços patch.importerror = No s’ha pogut importar el conjunt de pedaços. patch.delete.confirm = Esteu segur que voleu esborrar aquest conjunt de pedaços? -patch.path = Path: +patch.path = Camí: patch.fields = {0} camps bundle.lines = {0} lines -images = Images -asset.title = Data Patches & Assets -asset.guide = Usage Guide -asset.bundle.exists = A bundle with path '{0}' already exists. -asset.importzip = Import Asset Zip -asset.exportzip = Export Asset Zip -asset.image.importzip = Import Zip -asset.image.exportzip = Export Zip -asset.clearall = Clear All -asset.clearall.confirm = Are you sure that you want to clear [red]all[] assets from this map?\nThis includes images, audio, content, etc. -asset.path.invalid = Path is invalid! -asset.imports = Import Results -asset.imported = [accent]{0} assets imported. -asset.import.none = No valid asset files found in zip archive. -asset.content.errors = Content Errors -asset.content.add = Add Content -asset.content.type = Type: -asset.content.exists = [red]Content with that name already exists! -asset.content.badname = [red]Invalid name! Most symbols are not allowed. -asset.content.import.file = Import File -asset.content.import.clipboard = Import From Clipboard -asset.exists = An asset of that type with the same path/name already exists:\n[lightgray]{0} -asset.exists.audio = A sound/music asset with that name is already loaded:\n[lightgray]{0} -asset.broken = This asset file could not be located.\nThis can be caused by improper import of data, or an incorrectly exported save. -asset.audio.invalid = Audio file could not be loaded.\nEnsure that it is a valid OGG or MP3 file. -asset.delete.confirm = Are you sure you want to delete this asset? -asset.images = Data Patch Images -asset.image.imports = Import Results -asset.image.imported = [accent]{0} images imported. -asset.image.none = No valid PNG files found in zip archive. -asset.image.error = [red]The following errors were encountered:[] -asset.image.clearall.confirm = Are you certain that you wish to clear all images? -asset.image.info = Image Info -asset.image.toolarge = Image ([lightgray]{0}x{1}[]) is too large. Maximum size is {2}x{3}. -asset.image.exists = An image with that path/name already exists:\n[lightgray]{0} -asset.image.delete.confirm = Are you certain that you wish to delete this image? -asset.image.name = [accent]File Name: [lightgray]{0} -asset.image.region.name = [accent]Region Name: [lightgray]{0} -asset.image.path = [accent]Path: [lightgray]{0} -asset.image.env = [accent]Uses Environment Atlas: [lightgray]{0} -asset.image.size = [accent]Size: [lightgray]{0}x{1} -asset.image.filesize = [accent]File Size: [lightgray]{0} -asset.image.packfailed = [red]⚠ Failed to pack sprite![] -asset.image.packfailed.env = This is likely caused by a lack of space in the environment atlas.\nNote that environment sprites are limited to a 512x512 total area. +images = Imatges +asset.title = Recursos i pedaços de dades +asset.guide = Guia d’ús +asset.bundle.exists = Al camí «{0}» ja hi ha un paquet. +asset.importzip = Importa Zip de recursos +asset.exportzip = Exporta Zip de recursos +asset.image.importzip = Importa Zip +asset.image.exportzip = Exporta Zip +asset.clearall = Esborra’ls tots +asset.clearall.confirm = Esteu segur que voleu esborrar [red]tots[] els recursos del mapa?\nAixò inclou imatges, so, contingut, etc. +asset.path.invalid = El camí no és vàlid! +asset.imports = Importa resultats +asset.imported = [accent]{0} recursos importats. +asset.import.none = No hi ha fitxers de recursos al fitxer zip. +asset.content.errors = Errors de contingut +asset.content.add = Afegeix contingut +asset.content.type = Tipus: +asset.content.exists = [red]Ja existeix contingut amb aquest nom! +asset.content.badname = [red]Nom no vàlid. No es permet fer servir la majoria dels símbols. +asset.content.import.file = Importa fitxer +asset.content.import.clipboard = Importa del porta-retalls +asset.exists = Ja existeix un recurs del mateix tipus amb el mateix camí/nom:\n[lightgray]{0} +asset.exists.audio = Ja s’ha carregat un recurs de so/música amb aquest nom:\n[lightgray]{0} +asset.broken = Aquest fitxer de recurs no s’ha pogut localitzar.\nPot ser a causa d’una importació inadequada de dades o d’una desada exportada de manera incorrecta. +asset.audio.invalid = El fitxer de so no s’ha pogut carregar.\nAssegureu-vos que és un fitxer OGG o MP3 vàlid. +asset.delete.confirm = Esteu segur que voleu esborrar aquest recurs? +asset.images = Imatges del pedaç de dades +asset.image.imports = Importa els resultats +asset.image.imported = [accent]{0} imatges importades. +asset.image.none = No s’han trobat fitxers PNG vàlids al fitxer zip. +asset.image.error = [red]S’han produït els següents errors:[] +asset.image.clearall.confirm = Esteu segur que voleu esborrar totes les imatges? +asset.image.info = Informació de la imatge +asset.image.toolarge = La imatge ([lightgray]{0}×{1}[]) és massa gran. La mida màxima és {2}×{3}. +asset.image.exists = Ja existeix una imatge amb aquest camí/nom:\n[lightgray]{0} +asset.image.delete.confirm = Esteu segur que voleu esborrar aquesta imatge? +asset.image.name = [accent]Nom del fitxer: [lightgray]{0} +asset.image.region.name = [accent]Nom de la regió: [lightgray]{0} +asset.image.path = [accent]Camí: [lightgray]{0} +asset.image.env = [accent]Fa servir l’atles d’entorn: [lightgray]{0} +asset.image.size = [accent]Mida: [lightgray]{0}×{1} +asset.image.filesize = [accent]Mida del fitxer: [lightgray]{0} +asset.image.packfailed = [red]⚠ La imatge no s’ha pogut empaquetar![] +asset.image.packfailed.env = Normalment està provocat per una falta d’espai a l’atles d’entorn.\nTingueu en compte que les imatges es limiten a una àrea total de 512×512. asset.emoji.delete = Delete Selected ({0}) asset.emoji.notfound = Image with specified name not found. @@ -617,7 +617,7 @@ waves.spawn = aparèixer: waves.spawn.all = waves.spawn.select = Selecciona la zona d’aparició waves.spawn.none = [scarlet]No s’han trobat zones d’aparició al mapa. -waves.spawn.first = [lightgray](first {0}) +waves.spawn.first = [lightgray](primer {0}) waves.max = nombre màxim d’unitats waves.guardian = Guardià waves.preview = Previsualització @@ -631,10 +631,10 @@ waves.sort.begin = Comença waves.sort.health = Salut waves.sort.type = Tipus waves.search = Es busquen onades… -waves.filter = Filtre d'unitats +waves.filter = Filtre d’unitats waves.units.hide = Amaga-les totes waves.units.show = Mostra-les totes -pause.disabled = [scarlet]Pause is disabled +pause.disabled = [scarlet]La pausa està desactivada. #these are intentionally in lower case wavemode.counts = comptades @@ -645,9 +645,9 @@ all = Tots editor.default = [lightgray] details = Detalls edit = Edita -edit.menu = Edit... +edit.menu = Edita… variables = Variables -logic.error.toolong = Processor code too long (Limit: {0} compressed bytes, actual length: {1} bytes) +logic.error.toolong = El codi del processador és massa llarg (Límit: {0} bytes comprimits, llargada actual: {1} bytes) logic.clear.confirm = Esteu segur que voleu esborrar tot el codi d’aquest processador? logic.restart = Reinicia logic.globals = Variables internes @@ -723,8 +723,8 @@ toolmode.drawteams.description = Dibuixa els equips en lloc de dibuixar blocs. #unused toolmode.underliquid = Sota els líquids toolmode.underliquid.description = Dibuixa el terra a sota de les caselles de líquids. -toolmode.fillunderliquid = Fill Under Liquids -toolmode.fillunderliquid.description = Fill floors under liquid tiles. +toolmode.fillunderliquid = Omple sota els líquids +toolmode.fillunderliquid.description = Omple el terra a sota de les caselles de líquids. filters.empty = [lightgray]Sense filtres! Afegiu-ne un amb el botó de sota. @@ -771,7 +771,7 @@ filter.option.percentile = Percentil filter.option.code = Codi filter.option.loop = Bucle -locales.info = Aquí, podeu afegir paquets de traducció per a idiomes específics al vostre mapa. En els paquets de traducció, cada propietat té un nom i un valor. Aquestes propietats les poden fer servir els processadors integrats i els objectius, fent servir els seus noms. Suporten el format de text (reemplaçant els marcadors de posició amb els seus valors corresponents).\n\n[cyan]Exemple de propietat:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) +locales.info = Aquí, podeu afegir paquets de traducció per a idiomes específics al vostre mapa. En els paquets de traducció, cada propietat té un nom i un valor. Aquestes propietats les poden fer servir els processadors integrats i els objectius, fent servir els seus noms. Suporten el format de text (reemplaçant els marcadors de posició amb els seus valors corresponents).\n\n[cyan]Exemple de propietat:\n[]name: [accent]timer[]\nvalue: [accent]Exemple de cronòmetre i temps restant: {0}[]\n\n[cyan]Ús:\n[]Establiu-ho com a text d’objectiu: [accent]@timer\n\n[]Imprimeix-ho en un processador mundial:\n[accent]localeprint "timer"\nformat time\n[gray](on time és una variable calculada per separat) locales.deletelocale = Esteu segur que voleu esborrar aquesta traducció? locales.applytoall = Aplica els canvis a totes les traduccions locales.addtoother = Afegeix a les altres traduccions @@ -788,7 +788,7 @@ locales.showsame = Mostra les propietats que tenen els mateixos valors en traduc locales.viewproperty = Mostra en totes les traduccions locales.viewing = Es mostra la propietat «{0}» locales.addicon = Afegeix una icona -locales.property.name = name +locales.property.name = nom locales.property.text = text width = Amplada: @@ -863,7 +863,7 @@ objective.destroyunits = [accent]Destruïu [][lightgray]{0}[] unitats. objective.enemiesapproaching = [accent]Arribaran enemics d’aquí [lightgray]{0}[]. objective.enemyescalating = [accent]La producció enemiga augmentarà d’aquí [lightgray]{0}[] objective.enemyairunits = [accent]L’enemic començarà a produir unitats aèries d’aquí [lightgray]{0}[] -objective.enemyunitproduction = [accent]Enemy unit production begins in [lightgray]{0}[] +objective.enemyunitproduction = [accent]La producció d’unitats enemigues començarà d’aquí [lightgray]{0}[] objective.destroycore = [accent]Destruïu el nucli enemic. objective.command = [accent]Dirigiu les unitats. objective.nuclearlaunch = [accent]⚠ S’ha detectat un llançament nuclear: [lightgray]{0} @@ -951,7 +951,7 @@ sector.noswitch.title = Els sectors no es poden canviar. sector.noswitch = Potser no podeu canviar de sector perquè n’ataquen un altre.\n\nSector: [accent]{0}[] de [accent]{1}[] sector.view = Veure el sector sector.foundationrequired = [lightgray] Nucli: Calen fonaments -sector.shielded = [lightgray] Shielded +sector.shielded = [lightgray] Protegit amb escut threat.low = Baixa threat.medium = Mitjana @@ -959,9 +959,9 @@ threat.high = Alta threat.extreme = Extrema threat.eradication = Erradicació -difficulty.guide.title = Notice -difficulty.guide = If a sector poses too much of a challenge, campaign difficulty can be adjusted for an easier experience. -difficulty.guide.confirm = Adjust Difficulty +difficulty.guide.title = Avís +difficulty.guide = Si un sector és massa difícil, la dificultat de la campanya es pot ajustar per a gaudir d’una experiència més fàcil. +difficulty.guide.confirm = Ajusta la dificultat difficulty.casual = Molt fàcil difficulty.easy = Fàcil @@ -982,7 +982,7 @@ planet.sun.name = Sol sector.impact0078.name = Impacte 0078 sector.groundZero.name = Zona zero -sector.crateredBattleground.name = Cratered Battleground +sector.crateredBattleground.name = Camp de batalla amb cràters sector.frozenForest.name = El bosc gelat sector.ruinousShores.name = Costes en ruïnes sector.stainedMountains.name = Muntanyes tacades @@ -990,7 +990,7 @@ sector.desolateRift.name = L’escletxa desolada sector.nuclearComplex.name = La central de producció nuclear sector.overgrowth.name = Creixement exuberant sector.tarFields.name = Els camps de quitrà -sector.saltFlats.name = Les Salines +sector.saltFlats.name = Les salines sector.fungalPass.name = El port de muntanya dels fongs sector.biomassFacility.name = La central de síntesi de biomassa sector.windsweptIslands.name = Les illes escombrades pel vent @@ -1003,19 +1003,19 @@ sector.coastline.name = Línia de costa sector.navalFortress.name = Fortalesa naval sector.atolls.name = Els atol·lons sector.testingGrounds.name = Camps de proves -sector.perilousHarbor.name = Perilous Harbor +sector.perilousHarbor.name = Port perillós sector.weatheredChannels.name = Els canals erosionats -sector.fallenVessel.name = Fallen Vessel +sector.fallenVessel.name = La nau caiguda sector.mycelialBastion.name = Bastió micelial sector.frontier.name = La frontera sector.sunkenPier.name = Moll afonat -sector.littoralShipyard.name = Littoral Shipyard +sector.littoralShipyard.name = Drassana litoral sector.geothermalStronghold.name = Fortalesa geotèrmica sector.groundZero.description = El lloc adequat per a començar de nou. Amenaça enemiga baixa. Pocs recursos.\nRecolliu tot el coure i plom que pugueu.\nDesprés, continueu en un altre sector. sector.frozenForest.description = Les espores han arribat fins aquí, prop de les muntanyes. Les temperatures baixes no les podran contenir per sempre.\n\nComenceu el camí del poder. Construïu generadors a combustió. Apreneu a fer servir els reparadors. sector.saltFlats.description = Als límits del desert hi ha les Salines. Aquesta regió té pocs recursos.\n\nL’enemic hi ha aixecat un complex d’emmagatzematge de recursos. Elimineu el seu nucli i no en deixeu cap rastre. -sector.crateredBattleground.description = Water has accumulated in this crater, relic of the old wars. Reclaim the area. Collect sand. Smelt metaglass. Pump water to cool turrets and drills. +sector.crateredBattleground.description = En aquest cràter, relíquia d’una guerra de fa molt temps, s’hi ha acumulat aigua. Reclameu l’àrea. Recolliu sorra. Foneu metavidre. Bombegeu aigua per a refredar les torretes i les perforadores. sector.ruinousShores.description = Més enllà dels erms, hi ha la costa. En el seu temps, hi havia una línia de defensa costera. No en queda molt. Només hi queden intactes les estructures de defensa més bàsiques; de la resta només en queda ferralla.\nContinueu l’expansió i redescobriu tecnologies perdudes. sector.stainedMountains.description = Terra endins, hi ha muntanyes que no han estat contaminades per les espores.\nExtraieu el titani que abunda a l’àrea. Apreneu a usar-lo.\n\nEn aquesta zona hi ha més presència enemiga. No els deixeu temps per a enviar-vos les unitats més fortes. sector.overgrowth.description = En aquesta àrea s’ha produït un creixement desmesurat i està a prop de la font de les espores.\nL’enemic hi ha establit un post avançat. Construïu unitats [accent]Maça[] i destruïu-lo. @@ -1030,17 +1030,17 @@ sector.impact0078.description = Aquí hi ha les restes de la primera nau de tran sector.planetaryTerminal.description = L’objectiu final.\n\nAquesta base costera conté una estructura capaç de llançar nuclis a altres planetes. Està molt ben vigilida.\n\nProduïu unitats navals, elimineu l’enemic tan aviat com pugueu i investigueu l’estructura de llançament. sector.coastline.description = S’han detectat restes de tecnologia naval a prop. Repel·liu els atacs enemics, captureu el sector i aconseguiu la tecnologia. sector.navalFortress.description = L’enemic ha establert una base en una illa distant amb defenses geològiques naturals. Destruïu el post avançat i aconseguiu i investigueu les seves tecnologies navals avançades. -sector.facility32m.description = This sector contains the moldering remnants of an explosives production facility.\n\nCapture this area. Research the Blast Mixer. Produce Blast Compound for use as turret ammunition. -sector.taintedWoods.description = [gray][optional objective][]\nRich in oil reserves, this sector contains large masses of fungal growth, making it difficult to traverse.\n\nUtilize its resources to produce Plastanium. Fend off the enemy. -sector.sunkenPier.description = [gray][optional objective][]\nOnce an offshore oil platform, this facility has long-since fallen into disrepair.\n\nHigh amounts of enemy naval units detected. Be prepared for attacks from multiple directions. -sector.atolls.description = [gray][optional objective][]\nMultiple enemy naval bases have been detected at this location.\n\nUtilize Mega payload units to transport ground forces into attack positions. Build naval units to destroy the peripheral outposts. -sector.frontier.description = A small enemy outpost has been detected here.\n\nBuild Mono units in order to harvest resources more effectively. Utilize the Additive Reconstructor to construct stronger units. -sector.infestedCanyons.description = Beyond the mountains lies a winding river valley, now covered in layers of spore moss.\n\nHold your ground against the enemy onslaught. Construct naval units. Eliminate all enemy cores. -sector.testingGrounds.description = [gray][optional objective][]\nA crater-riddled desert valley, formerly used as a testing ground for military projects.\n\nDue to lack of water, establishing a presence in this location may pose a challenge. Be prepared for attacks from multiple directions. -sector.perilousHarbor.description = Multiple enemy bases have been established across this treacherous coastal region.\n\nConstruct naval units, and crush their fortifications. -sector.weatheredChannels.description = [gray][optional objective][]\nThis channel system harbors abundant resources. Land your core. Fend off the enemy waves to claim them.\n\nThe enemy will take multiple routes to assault your infrastructure. Look for signs of a possible ambush and defend accordingly. -sector.mycelialBastion.description = This fortified complex lies deep within the fungal regions, threatening neighboring sectors with advanced insectoid units.\n\nAssault the complex. Obtain the technology within. Utilize units capable of traversing walls. -sector.littoralShipyard.description = Located upon the far reaches of the central sea, this naval production facility contains technology for building advanced units.\n\nDestroy this facility, and use its technology to further assist in the completion of the final objective. +sector.facility32m.description = Aquest sector conté les restes d’unes instal·lacions de producció d’explosius.\n\nCaptureu l’àrea, estudieu el barrejador d’explosius i produïu compost explosiu per a fer servir de munició per a les torretes. +sector.taintedWoods.description = [gray][Objectiu opcional][]\nAquest sector és ric en reserves petrolíferes i grans masses de creixement fúngic i és difícil de travessar.\n\nFeu-ne servir els recursos per a produir plastani. Defenseu-vos dels enemics. +sector.sunkenPier.description = [gray][Objectiu addicional][]\nAquestes instal·lacions ancestrals, una plataforma petrolífera a la costa, han caigut en l’oblit.\n\nS’hi han detectat grans quantitats d’enemics navals. Prepareu-vos per als atacs des de múltiples direccions. +sector.atolls.description = [gray][Objectiu addicional][]\nEn aquest lloc s’hi han trobat diverses bases navals.\n\nFeu servir transportadors grossos de blocs a distància per a transportar les unitats terrestres fins a les posicions d’atac. Construïu unitats navals per a destruir els posts avançats perifèrics. +sector.frontier.description = S’ha detectat un petit post avançat enemic.\n\nConstruïu unitats mono per tal de recollir recursos més efectivament. Feu servir reconstructors additius per a construir unitats més fortes. +sector.infestedCanyons.description = Sota les muntanyes hi ha una vall amb un riu, coberta per capes de molsa d’espores.\n\nMantingueu la vostra posició contra l’assalt enemic. Construïu unitats navals. Elimineu els nuclis enemics. +sector.testingGrounds.description = [gray][Objectiu addicional][]\nUna vall desèrtica amb cràters que abans es feia servir com a lloc de proves militars.\n\nDegut a la falta d’aigua, establir-hi una presència estable pot ser un repte. Prepareu-vos per a atacs des de múltiples direccions. +sector.perilousHarbor.description = En aquesta regió costera, s’hi han instal·lat diverses bases enemigues.\n\nConstruïu unitats navals i destrosseu les seves fortificacions. +sector.weatheredChannels.description = [gray][optional objective][]\nAquest sistema de canals té recursos abundants. Instsal·leu-hi un nucli i defenseu-vos de les onades enemigues per a reclamar-lo.\n\nL’enemic farà servir diferents rutes per a assaltar les vostres infraestructures. Busqueu senyals d’una possible emboscada i defenseu-vos bé. +sector.mycelialBastion.description = Aquest complex fortificat està ben endins de les regions fúngiques i amenaça els sectors veïns amb unitats insectoides avançats.\n\nAssalteu el complex i obteniu les tecnologies que hi hagi. Feu servir unitats capaces de travessar murs. +sector.littoralShipyard.description = Ubicat als llocs més llunyans d’un mar central, aquestes instal·lacions de producció naval té tecnologies per a construir unitats.\n\nDestruïu les instal·lacions i feu servir les seves tecnologies per a assistir en l’assoliment de l’objectiu final. sector.onset.name = El principi sector.aegis.name = L’ègida @@ -1094,10 +1094,10 @@ status.blasted.name = Explosionat status.corroded.name = Oxidat status.unmoving.name = Immòbil status.boss.name = Guardià -status.slow.name = Slow -status.fast.name = Fast -status.shielded.name = Shielded -status.disarmed.name = Disarmed +status.slow.name = Lent +status.fast.name = Ràpid +status.shielded.name = Amb escut +status.disarmed.name = Sense armes settings.language = Idioma settings.data = Dades del joc @@ -1107,7 +1107,7 @@ settings.resetKey = Restableix settings.controls = Controls settings.game = Joc settings.sound = So -settings.dev = Developer Options +settings.dev = Opcions de desenvolupament settings.graphics = Gràfics settings.cleardata = Esborra les dades de la partida settings.clear.confirm = Esteu segur que voleu esborrar les dades?\nAquesta acció no es pot desfer. @@ -1118,17 +1118,17 @@ settings.clearresearch = Esborra la recerca settings.clearresearch.confirm = Esteu segur que voleu esborrar tota la recerca feta en el mode campanya? settings.clearcampaignsaves = Esborra les desades de la campanya settings.clearcampaignsaves.confirm = Esteu segur que voleu esborrar totes les desades de la campanya? -settings.clearplanetdata = Clear Planet Data -settings.planetselect = Planet: {0} -settings.clearplanetresearch = Clear Planet Research -settings.clearplanetresearch.confirm = Are you sure you want to clear {0}'s research? -settings.clearplanetcampaignsaves = Clear Planet Campaign Saves -settings.clearplanetcampaignsaves.confirm = Are you sure you want to clear {0}'s campaign saves? +settings.clearplanetdata = Esborra les dades del planeta +settings.planetselect = Planeta: {0} +settings.clearplanetresearch = Esborra la recerca del planeta +settings.clearplanetresearch.confirm = Esteu segur que voleu esborrar la recerca de {0}? +settings.clearplanetcampaignsaves = Esborra les desades de la campanya del planeta +settings.clearplanetcampaignsaves.confirm = Esteu segur que voleu esborrar les desades de la campanya de {0}? paused = [accent]< En pausa > clear = Esborra banned = [scarlet]Expulsat -block.limit = [scarlet]Limit: {0} Buildings +block.limit = [scarlet]Límit: {0} estructures unsupported.environment = [scarlet]Entorn no vàlid yes = Sí no = No @@ -1158,7 +1158,7 @@ stat.targetsground = Dispara objectius terrestres stat.crushdamage = Dany per aixafament stat.legsplashdamage = Dany per aixafament stat.itemsmoved = Velocitat de moviment -stat.itemsmoved.info = Throughput is often not linear and tied to FPS/TPS. +stat.itemsmoved.info = El rendiment sovint no és lineal i va lligat als FPS/TPS. stat.launchtime = Temps entre llançaments stat.shootrange = Abast stat.size = Mida @@ -1173,17 +1173,17 @@ stat.powerdamage = Energia/Dany stat.itemcapacity = Capacitat d’elements stat.memorycapacity = Capacitat de memòria stat.basepowergeneration = Generació base d’energia -stat.meltdowntime = Meltdown Time +stat.meltdowntime = Temps de fusió stat.productiontime = Temps de producció -stat.warmuptime = Warmup Time +stat.warmuptime = Temps de preescalfament stat.repairtime = Temps de reparació completa de blocs stat.repairspeed = Velocitat de reparació stat.weapons = Armes stat.bullet = Bala stat.moduletier = Nivell del mòdul stat.unittype = Tipus d’unitat -stat.receiverate = Receiving Rate -stat.receiverate.info = Affected by overdrive. Throughput can be increased by placing additional unloaders. +stat.receiverate = Rati de recepció +stat.receiverate.info = Afectat per l’acceleració. El rendiment es pot millorar posant-hi descarregadors addicionals. stat.speedincrease = Augment de velocitat stat.range = Abast stat.drilltier = Perforables @@ -1192,7 +1192,7 @@ stat.boosteffect = Efecte del potenciador stat.maxunits = Unitats actives màximes stat.health = Salut stat.armor = Armadura -stat.armor.info = Applied damage = incoming damage - armor.\nUp to 90% maximum damage reduction. +stat.armor.info = Dany aplicat = dany rebut - escut.\nFins a una reducció màxima del 90 %. stat.buildtime = Temps de construcció stat.maxconsecutive = Màxim consecutiu stat.buildcost = Cost de construcció @@ -1221,7 +1221,7 @@ stat.minetier = Nivell de perforació stat.payloadcapacity = Capacitat de les cintes de blocs stat.abilities = Habilitats stat.canboost = Pot sobrevolar. -stat.boostingspeed = Boosting Speed +stat.boostingspeed = Velocitat de sobrevolar stat.flying = Està volant. stat.ammouse = Ús de munició stat.ammocapacity = Capacitat de munició @@ -1305,8 +1305,8 @@ bar.liquid = Líquid bar.heat = Calor bar.cooldown = Temps de refredament bar.instability = Inestabilitat -bar.whenfull = when full -bar.nevermelts = never melts +bar.whenfull = quan està ple +bar.nevermelts = mai es fon bar.heatamount = Calor: {0} bar.heatpercent = Calor: {0} ({1} %) bar.power = Potència @@ -1317,37 +1317,37 @@ bar.input = Entrada bar.output = Sortida bar.strength = [stat]{0}[lightgray]× força bar.regenerationrate = [stat]{0}/sec[lightgray] de taxa de regen. -bar.activationtimer = Activates in {0} -bar.activated = Activated +bar.activationtimer = S’activa d’aquí a {0} +bar.activated = Activat units.processorcontrol = [lightgray]Controlat pel processador -weapon.pointdefense = [stat]Point Defense +weapon.pointdefense = [stat]Punt de defensa bullet.damage = [stat]{0}[lightgray] de dany bullet.splashdamage = [stat]{0}[lightgray] de dany a l’àrea ~[stat] {1}[lightgray] caselles bullet.incendiary = [stat]incendiari bullet.homing = [stat]munició guiada bullet.armorpierce = [stat]perforador d’armadures -bullet.armorweakness = [red]{0}x[lightgray] armor weakness -bullet.partialarmorpierce = [stat]{0}%[lightgray] armor piercing -bullet.antiarmor = [stat]{0}x[lightgray] anti-armor -bullet.blockarmorweakness = [red]{0}x[lightgray] building armor weakness -bullet.blockpartialarmorpierce = [stat]{0}%[lightgray] building armor pierce -bullet.blockantiarmor = [stat]{0}x[lightgray] building anti-armor -bullet.maxdamagefraction = [stat]{0}%[lightgray] de dany límit +bullet.armorweakness = [red]{0}×[lightgray] de debilitat d’armadura +bullet.partialarmorpierce = [stat]{0} %[lightgray] de perforació d’armadura +bullet.antiarmor = [stat]{0}×[lightgray] d’antiarmadura +bullet.blockarmorweakness = [red]{0}×[lightgray] de debilitat d’armadura de l’estructura +bullet.blockpartialarmorpierce = [stat]{0} %[lightgray] de performació d’armadura de l’estructura +bullet.blockantiarmor = [stat]{0}×[lightgray] d’antiarmadura de l’estructura +bullet.maxdamagefraction = [stat]{0} %[lightgray] de dany límit bullet.suppression = [stat]Supressió de reparacions cada {0} s[lightgray] ~ [stat]{1}[lightgray] caselles -bullet.empradius = [stat]{0}[lightgray] tiles EMP radius -bullet.empboost = [stat]{0}%[lightgray] overdrive ~ [stat]{1}[lightgray] -bullet.empdamage = [stat]{0}%[lightgray] power damage -bullet.empslowdown = [stat]{0}%[lightgray] enemy power overdrive ~ [stat]{1}[lightgray] -bullet.empunitdamage = [stat]{0}%[lightgray] unit damage +bullet.empradius = [stat]{0}[lightgray] caselles de radi PEM +bullet.empboost = [stat]{0} %[lightgray] d’acceleració ~ [stat]{1}[lightgray] +bullet.empdamage = [stat]{0} %[lightgray] de potència de dany +bullet.empslowdown = [stat]{0} %[lightgray] d’acceleració de potència enemiga ~ [stat]{1}[lightgray] +bullet.empunitdamage = [stat]{0} %[lightgray] de dany d’unitats bullet.interval = [stat]Interval de bales de {0}/s[lightgray]: bullet.frags = [stat]{0}[lightgray]× de bales de fragmentació: bullet.lightning = [stat]{0}[lightgray]× llampec ~ [stat]{1}[lightgray] de dany -bullet.lightninginterval = [stat]{0}[lightgray] tiles interval ~ [stat]{1}[lightgray] tiles length +bullet.lightninginterval = [stat]{0}[lightgray] d’interval de caselles ~ [stat]{1}[lightgray] de llargada de caselles bullet.buildingdamage = [stat]{0} %[lightgray] de dany a les estructures -bullet.spawnBullets = [stat]{0}x[lightgray] spawned bullets: +bullet.spawnBullets = [stat]{0}×[lightgray] bales creades: bullet.shielddamage = [stat]{0} %[lightgray] de dany d’escut bullet.knockback = [stat]{0}[lightgray] de bloqueig bullet.pierce = [stat]{0}[lightgray]× de perforació @@ -1368,7 +1368,7 @@ unit.liquidsecond = unitats de líquid/s unit.itemssecond = elements/s unit.liquidunits = unitats de líquid unit.powerunits = unitats d’energia -unit.powerequilibrium = power equilibrium +unit.powerequilibrium = potència d’equilibri unit.heatunits = unitats de calor unit.degrees = ° unit.seconds = s @@ -1376,17 +1376,17 @@ unit.minutes = min unit.persecond = /s unit.perminute = /min unit.timesspeed = × velocitat -unit.multiplier = x +unit.multiplier = × unit.percent = % unit.shieldhealth = salut d’escut unit.items = elements unit.thousands = k unit.millions = M unit.billions = kM -unit.shots = dispars -unit.pershot = /dispar +unit.shots = trets +unit.pershot = /tret unit.perleg = per pota -unit.perside = per side +unit.perside = per banda category.purpose = Funció category.general = General category.power = Energia @@ -1412,8 +1412,8 @@ setting.modcrashdisable.name = Desactiva els mods quan no es pugui iniciar el jo setting.animatedwater.name = Animacions del terreny setting.animatedshields.name = Animacions dels escuts setting.playerindicators.name = Indicadors de jugadors -setting.showpings.name = Show Pings -setting.showotherbuildplans.name = Show Other Player's Build Plans +setting.showpings.name = Mostra els pings +setting.showotherbuildplans.name = Mostra els plans de construcció dels altres jugadors setting.indicators.name = Indicadors d’enemics setting.autotarget.name = Apunta automàticament setting.keyboard.name = Controls amb ratolí i teclat @@ -1423,8 +1423,8 @@ setting.fpscap.none = Cap setting.fpscap.text = {0} FPS setting.uiscale.name = Escala de la interfície setting.uiscale.description = Cal reiniciar perquè s’apliquin els canvis. -setting.uiEdgePadding.name = UI Edge Padding -setting.uiEdgePadding.description = Adds padding to the edges of the UI. Useful for displays with rounded corners or notches. +setting.uiEdgePadding.name = Farciment de les vores de la interfície +setting.uiEdgePadding.description = Afegeix farciment a les vores de la interfície. Útil per a pantalles amb vores arrodonides o formes estranyes. setting.swapdiagonal.name = Permet sempre construir en diagonal setting.screenshake.name = Sacseig de pantalla setting.bloomintensity.name = Intensitat de l’efecte «bloom» @@ -1440,10 +1440,10 @@ setting.milliseconds = {0} ms setting.fullscreen.name = Pantalla completa setting.fps.name = Mostra els FPS i el ping setting.console.name = Activa la consola -setting.drawhitboxes.name = Display Hitboxes -setting.showperformance.name = Display Performance Metrics -setting.buildingtimestep.name = Fixed Building Timestep -setting.buildingtimestep.description = Runs buildings on a fixed timestep with constant delta,\nas opposed to every frame with variable delta.\nIf buildings cannot be updated fast enough, the game will slow down.\nIf you don't know what that means, don't touch this option. +setting.drawhitboxes.name = Mostra quadres d’impacte +setting.showperformance.name = Mostra mesures de rendiment +setting.buildingtimestep.name = Pas de temps de les estructures fix +setting.buildingtimestep.description = Les estructures s’executen a passos de temps fixos amb una delta constant,\na diferència de fer-ho amb deltes diferents.\nSi les estructures no es poden actualitzar prou ràpid, el joc s’alentirà.\nSi no enteneu què significa aquesta opció, no la toqueu. setting.smoothcamera.name = Moviment de càmera suau setting.detach-camera.name = Càmera lliure setting.detach-camera.description = Si s’activa, la càmera es mou independentment de la unitat del jugador.\nA aquesta opció se li pot assignar una tecla de drecera. @@ -1492,7 +1492,7 @@ category.multiplayer.name = Multijugador category.blocks.name = Selecció d’estructures per construir placement.blockselectkeys = \n[lightgray]Tecles: [{0}, ping.text = Ping: -keybind.ping.name = Ping Location +keybind.ping.name = Ubicació del ping keybind.respawn.name = Reapareix keybind.control.name = Controla una unitat keybind.clear_building.name = Desmunta una estructura @@ -1516,8 +1516,8 @@ keybind.unit_stance_hold_fire.name = Comportament: Mantén el foc keybind.unit_stance_pursue_target.name = Comportament: Persegueix l’objectiu keybind.unit_stance_patrol.name = Comportament: Patrulla keybind.unit_stance_ram.name = Comportament: Senzill -keybind.unit_stance_boost.name = Unit Stance: Boost -keybind.unit_stance_hold_position.name = Unit Stance: Hold Position +keybind.unit_stance_boost.name = Comportament: Sobrevola +keybind.unit_stance_hold_position.name = Comportament: Manté la posició keybind.unit_command_move.name = Ordre d’unitat: Mou keybind.unit_command_repair.name = Ordre d’unitat: Repara @@ -1586,9 +1586,9 @@ keybind.chat_mode.name = Canvia el mode del xat keybind.drop_unit.name = Deixa anar la unitat keybind.zoom_minimap.name = Apropa/Allunya la vista del minimapa keybind.detach_camera.name = Commuta la càmera lliure -keybind.teleport_cursor.name = Teleport To Cursor (Sandbox) +keybind.teleport_cursor.name = Teleporta al cursor (en mode lliure) keybind.debug_hitboxes.name = Commuta les caixes de xoc de depuració -keybind.performance_metrics.name = Toggle Performance Metrics +keybind.performance_metrics.name = Commuta les mesures de rendiment mode.help.title = Descripció dels modes mode.survival.name = Supervivència mode.survival.description = El mode normal. Hi ha recursos limitats i les onades són automàtiques.\n[gray]Cal que al mapa hi hagin llocs d’aparició d’enemics. @@ -1645,15 +1645,15 @@ rules.checkplacement.info = Mentre estigui desactivat, les estructures d’aques rules.enemyCheat = Recursos infinits per a la IA (equip vermell) rules.blockhealthmultiplier = Multiplicador de la salut dels blocs rules.blockdamagemultiplier = Multiplicador del dany dels blocs -rules.unitfactoryactivation = Unit Factory Activation Delay -rules.unitfactoryactivation.objective = Unit Factories Activating: {0} +rules.unitfactoryactivation = Retard de l’activació de la fàbrica d’unitats +rules.unitfactoryactivation.objective = S’activen les fàbriques d’unitats: {0} rules.unitbuildspeedmultiplier = Multiplicador de la velocitat de producció d’unitats rules.unitcostmultiplier = Multiplicador del cost de les unitats rules.unithealthmultiplier = Multiplicador de la salut de les unitats rules.unitdamagemultiplier = Multiplicador del dany de les unitats rules.unitcrashdamagemultiplier = Multiplicador del dany de xoc de les unitats rules.unitminespeedmultiplier = Multiplicador de velocitat d’extracció de les unitats -rules.logicunitcontrol = Logic Unit Control +rules.logicunitcontrol = Control d’unitats lògiques rules.logicunitbuild = Construcció d’unitats lògiques rules.logicunitdeconstruct = Desmantellament d’unitats lògiques rules.solarmultiplier = Multiplicador de l’energia solar @@ -1683,23 +1683,23 @@ rules.title.environment = Entorn rules.title.teams = Equips rules.title.planet = Planeta rules.title.music = Music -rules.alwaysplaymusic = Always Play Music -rules.alwaysplaymusic.info = Overrides the 'always play music' audio setting. -rules.ambientmusic = Ambient Music -rules.ambientmusic.info = Comma-separated music tracks that randomly play under normal circumstances.\nIf empty, default music will be used. -rules.darkmusic = Conflict Music -rules.darkmusic.info = Comma-separated music tracks that are more likely to play during battle.\nIf empty, default music will be used. -rules.nomusic = Disable Automatic Music -rules.nomusic.info = Turns off all automatic music playback.\nUseful for maps that have custom music played using processors. +rules.alwaysplaymusic = Reprodueix música sempre +rules.alwaysplaymusic.info = Sobreescriu l’opció de so de «Reprodueix música sempre». +rules.ambientmusic = Música ambiental +rules.ambientmusic.info = Peces de música separades amb comes que es reproduiran aleatòriament en situacions normals.\nSi es deixa buit, es farà servir la música per defecte. +rules.darkmusic = Música de conflicte +rules.darkmusic.info = Peces de música separades amb comes que es reproduiran aleatòriament durant les batalles.\nSi es deixa buit, es farà servir la música per defecte. +rules.nomusic = Desactiva la música automàtica +rules.nomusic.info = Apaga la reproducció de la música automàtica.\nÚtil en mapes que ja tenen la seva música personalitzada mitjançant l’ús de processadors. rules.lighting = Il·luminació rules.fog = Amaga el terreny inexplorat -rules.invasions = Invasions de setors enemics +rules.invasions = Invasions de sectors enemics rules.legacylaunchpads = Mecàniques de plataformes de llançament antigues rules.legacylaunchpads.info = Permet fer servir plataformes de llançament sense plataformes d’aterratge, com a la versió 7.0. landingpad.legacy.disabled = [scarlet]\ue815 Desactivat[lightgray] (plataformes de llançament antigues activades) -rules.hidespawns = Hide Enemy Spawns +rules.hidespawns = Amaga les reaparicions d’enemics rules.randomwaveai = IA d’onades immprevisible -rules.pauseDisabled = Disable Pausing +rules.pauseDisabled = Desactiva les pauses rules.fire = Foc rules.anyenv = rules.explosions = Dany de les explosions als blocs/unitats @@ -2221,9 +2221,9 @@ block.disperse.name = Disperse block.afflict.name = Afflict block.lustre.name = Lustre block.scathe.name = Scathe -block.scathe.carbide.info = Travels significantly faster.\nModerate damage at any range. -block.scathe.surge-alloy.info = Splits into micro missiles.\nHeavy damage at close distance. -block.scathe.phase-fabric.info = Equipped with a force field.\nLow damage, largest radius. +block.scathe.carbide.info = Viatja significativament més de pressa.\nDany moderat a qualsevol abast. +block.scathe.surge-alloy.info = Se separa en micromíssils.\nDany elevat a distàncies curtes. +block.scathe.phase-fabric.info = Equipat amb un camp de força.\nPoc dany, però abast llarg. block.tank-refabricator.name = Milloradora de tancs block.mech-refabricator.name = Milloradora de meques block.ship-refabricator.name = Milloradora de naus @@ -2300,7 +2300,7 @@ hint.payloadDrop.mobile = [accent]Manteniu premuda[] una posició buida per a de hint.waveFire = Les torretes de tipus [accent]Wave[] que usin aigua com munició apagaran els focs propers automàticament. hint.guardian = Les unitats de tipus [accent]Guardià[] són unitats blindades. La munició dèbil com ara el [accent]Coure[] i el [accent]Plom[] [scarlet]no és efectiva[].\n\nEmpreu torretes de nivell més alt o carregueu torretes :duo:Duo/:salvo:Salvo amb :graphite: [accent]Grafit[] per a destruir-los. hint.coreUpgrade = Els nuclis es poden millorar [accent]construint-hi a sobre nuclis amb millors característiques[].\n\nSitueu un nucli de tipus [accent]Fonament[] a sobre del nucli [accent]Estella[]. Assegureu-vos que no hi hagin obstruccions properes. -hint.serpuloCoreZone = Additional :core-shard: [accent]Cores[] may be constructed on :core-zone: [accent]Core Zone[] tiles. +hint.serpuloCoreZone = Es poden construir [accent]nuclis[] :core-shard: addicionals en caselles de [accent]zona de nucli[] tiles :core-zone: . hint.cannotUpgrade = Una icona [red]:tree:[] al damunt d’una unitat de bloc indica que la seva versió millorada encara no s’ha investigat.\n\nLes millores d’unitats s’han d’investigar a l’[accent]arbre de tecnologies :tree:[] abans que es puguin produir als reconstructors. hint.presetLaunch = Als [accent]sectors amb zones d’aterratge[] de color gris, com ara [accent]El bosc gelat[], s’hi pot accedir des de qualsevol lloc. No cal capturar cap territori proper.\n\nEls [accent]sectors numerats[], com aquest, són [accent]opcionals[]. hint.presetDifficulty = Aquest sector té un [accent]nivell d’amenaça enemiga elevat[].\n[accent]No es recomana[] aterrar a aquests sectors sense les tecnologies i la preparació adequades. @@ -2329,43 +2329,43 @@ gz.zone2 = Tot el que es construeixi a dins es destruirà quan comenci la proper gz.zone3 = Ara comença una onada.\nPrepareu-vos. gz.finish = Construïu més torretes, extraieu més recursos \ni defense-vos contra totes les onades per a [accent]capturar el sector[]. -ff.coal = Use :mechanical-drill: [accent]Mechanical Drills[] to mine :ore-coal: [accent]coal[]. -ff.graphitepress = :tree: Research and place a :graphite-press: [accent]Graphite Press[]. -ff.craft = Move :coal: coal into the :graphite-press: [accent]Graphite Press[].\nIt will output :graphite: graphite to all nearby conveyors.\nMove the output :graphite: graphite from the :graphite-press: [accent]Graphite Press[] into the core. -ff.generator = :coal: Coal can also be used as fuel in :combustion-generator: [accent]Combustion Generators[].\nResearch and place a :combustion-generator: [accent]Combustion Generator[]. -ff.coalpower = Input :coal: coal into the generator to produce [accent]:power: power[]. -ff.coalpower.objective = :combustion-generator: Produce Power -ff.node = :power-node: [accent]Power Nodes[] transmit power to nearby blocks in range.\nResearch and place a :power-node: [accent]Power Node[] close to the generator. -ff.battery = :battery: [accent]Batteries[] store power.\nResearch and place a :battery: [accent]Battery[] close to the node. -ff.arc = :arc: [accent]Arc[] turrets require power to function. Research and place an :arc: [accent]Arc[] turret near a power node. +ff.coal = Feu servir [accent]perforadores mecàniques[] :mechanical-drill: per a extraure :ore-coal: [accent]carbó[]. +ff.graphitepress = :tree: Investigueu i instal·leu una [accent]premsa de grafit[] :graphite-press: . +ff.craft = Transporteu carbó :coal: fins a la [accent]premsa de grafit[] :graphite-press: .\nProduirà grafit :graphite:, que sortirà per totes les cintes transportadores adjacents.\nTransporteu el grafit :graphite: de la [accent]premsa de grafit[] :graphite-press: al nucli. +ff.generator = El carbó :coal: també es pot fer servir com a combustible als [accent]generadors a combustió[] :combustion-generator: .\nInvestigueu i instal·leu un [accent]generador a combustió[] :combustion-generator: . +ff.coalpower = Poseu-hi carbó :coal: a dins del generador per a produir [accent]power :power: []. +ff.coalpower.objective = :combustion-generator: Produeix energia. +ff.node = Els [accent]nodes d’energia[] :power-node: transmeten energia als blocs que tinguin a l’abast.\nInvestigueu i instal·leu un [accent]node d’energia[] :power-node: a prop del generador. +ff.battery = Les [accent]bateries[] :battery: emmagatzemen energia.\nInvestigueu i instal·leu una [accent][bateria] a prop del node. +ff.arc = Les [accent]torretes[] :arc: necessiten energia per a funcionar. Investigueu i instal·leu una [accent]torreta[] :arc: a prop d’un node d’energia. -fungalpass.tutorial1 = Use [accent]units[] to defend buildings and attack the enemy.\nResearch and place a :ground-factory: [accent]ground factory[]. -fungalpass.tutorial2 = Select :dagger: [accent]Dagger[] units in the factory.\nProduce 3 units. +fungalpass.tutorial1 = Feu servir [accent]unitats[] per a defensar les estructures i atacar l’enemic.\nInvestigueu i instal·leu una :ground-factory: [accent]fàbrica d’unitats terrestres[]. +fungalpass.tutorial2 = Seleccioneu unitats [accent]daga[] :dagger: a la fàbrica.\nProduïu-ne 3. -frontier.tutorial1 = The :additive-reconstructor: [accent]Additive Reconstructor[]\nupgrades tier 1 units to tier 2\nusing :silicon: silicon and :graphite: graphite.\n\n[accent]Research and reconstruct\na []:dagger:[accent] dagger to a []:mace:[accent] mace. -frontier.tutorial2 = Enemy waves send [accent]infinitely[] until all enemy cores are [unlaunched]destroyed. -frontier.tutorial3 = [accent]Attack the enemy base fast to prevent waves from getting too high[]. +frontier.tutorial1 = El [accent]reconstructor additiu[] :additive-reconstructor: \nmillora el nivell 1 de les unitats al nivell 2\namb silici :silicon: i grafit :graphite: .\n\n[accent]Investigueu i reconstruïu\nuna [accent]daga[] :dagger: a [accent]maça[] :mace: . +frontier.tutorial2 = Les onades d’enemics s’envien [accent]indefinidament[] fins que tots els nuclis enemic s’han [unlaunched]destruït. +frontier.tutorial3 = [accent]Ataqueu la base enemiga de pressa per a evitar que les onades creixin massa[]. -atolls.destroy1 = Destroy the 2 enemy [accent]foundation cores[] first.\n[accent]Gain access to thorium[]. -atolls.mega1 = Enter [accent]command mode[] and select the :mega:[accent]Mega[] units. -atolls.mega2 = Select the \ue87b [accent]Load Units[] command to have Mega units to pick up ground units. -atolls.mega3 = Move the Mega units [accent]above[] ground units to load them in. -atolls.mega4 = [accent]Drop ground units here to attack. -atolls.mega5 = Select the \ue879 [accent]Unload Payload[] command to have Mega units drop their carried units. -atolls.mega6 = Units will be [accent]automatically dropped[] while the \ue879 Unload Payload command is selected. -atolls.mega7 = Mega units [accent]will not[] drop ground units while they are above walls or deep water. -atolls.mega8 = [accent]Drop naval units here to attack.[] -atolls.attack1 = 1: Drop ground units here to attack from this location. -atolls.attack2 = 2: Alternatively, drop naval units here to launch a surprise attack. -atolls.carry1 = Carrying ground units requires :mega:[accent]Mega[] units. -atolls.carry2 = 1: Command Mega units to [accent]move over[] the ground units. -atolls.carry3 = 2: Select the \ue87b [accent]Load Units[] command in order to pick up the ground units with the Mega units. -atolls.carry4 = 3: Command and move the Mega carriers [accent]loaded with units[]. -atolls.carry5 = 4: Select the \ue879 [accent]'Unload Payload'[] command in order for the Mega units to drop the ground units. -atolls.carry6 = These steps can be reviewed [accent]here[]. -atolls.carry7 = Follow these instructions to airdrop more units near the enemy base. -atolls.noairunit = This base is too well-fortified to destroy with air units. -atolls.2strategies = There are two strategies to destroy this base. +atolls.destroy1 = Abans, destruïu els 2 [accent]nuclis fonament[] .\n[accent]Heu d’obtenir accés al tori[]. +atolls.mega1 = Entreu al [accent]mode d’ordres[] i trieu les [accent]megaunitats[] :mega: . +atolls.mega2 = Seleccioneu l’ordre \ue87b [accent]Carrega unitats[] per tal que les megaunitats recullin les unitats terrestres. +atolls.mega3 = Moveu les megaunitats [accent]al damunt[] de les unitats terrestres per a carregar-les. +atolls.mega4 = [accent]Deixeu les unitats terrestres aquí per a atacar. +atolls.mega5 = Seleccioneu l’ordre \ue879 [accent]Descarrega[] per tal que les megaunitats deixin al terra les unitats que transporten. +atolls.mega6 = Les unitats [accent]es deixaran automàticament[] si l’ordre \ue879 Descarrega està activada. +atolls.mega7 = Les megaunitats [accent]no deixen[] les unitats terrestres al terra si estan al damunt de murs o d’aigües profundes. +atolls.mega8 = [accent]Deixeu les unitats navals aquí per a atacar.[] +atolls.attack1 = 1: Deixeu les unitats terrestres aquí per a atacar des d’aquest lloc. +atolls.attack2 = 2: També podeu deixar les unitats navals aquí i fer un atac sorpresa. +atolls.carry1 = Per a transportar unitats terrestre calen [accent]mega unitats[] :mega: . +atolls.carry2 = 1: Ordeneu a les megaunitats a [accent]moure[] les unitats terrestres. +atolls.carry3 = 2: Seleccioneu l’ordre \ue87b [accent]Carrega unitats[] per tal de recollir les unitats terrestres a les megaunitats. +atolls.carry4 = 3: Dirigiu i moveu els mega transports [accent]carregats d’unitats[]. +atolls.carry5 = 4: Seleccioneu l’ordre \ue879 [accent]Descarrega[] per tal que les megaunitats deixin al terra les unitats terrestres. +atolls.carry6 = Aquests passos es poden repassar [accent]aquí[]. +atolls.carry7 = Seguiu aquestes instruccions per a llançar més unitats paracaigudistes a prop de la base enemiga. +atolls.noairunit = Aquesta base està massa fortificada per a destruir-la amb unitats aèries. +atolls.2strategies = Hi ha dues estratègies per a destruir aquesta base. onset.mine = Feu clic als murs per a extraure [accent]beril·li[] :beryllium:.\n\nFeu servir [accent][[WASD] per a moure-vos. onset.mine.mobile = Toqueu per a extraure [accent]beril·li[] :beryllium: dels murs. @@ -2381,7 +2381,7 @@ onset.arcfurnace = El forn d’arc necessita :sand: [accent]sorra[] i :graphite: onset.crusher = Feu servir les :cliff-crusher: [accent]picadores d’espadats[] per a extraure sorra. onset.fabricator = Feu servir [accent]unitats[] per a explorar el mapa, defensar estructures i atacar als enemics. Investigueu i construïu una :tank-fabricator: [accent]fabricadora de tancs[]. onset.makeunit = Produïu una unitat.\nFeu servir el botó «?» per a veure els requisits de la fàbrica que trieu. -onset.turrets = Les unitats són efectives, però les [accent]torretes[] proporcionen una capacitat defensiva millor si es fan servir adequadament.\nConstruïu Place una torreta :breach: [accent]breach[].\nLes torretes necessiten :beryllium: [accent]munició[]. +onset.turrets = Les unitats són efectives, però les [accent]torretes[] proporcionen una capacitat defensiva millor si es fan servir adequadament.\nConstruïu una torreta :breach: [accent]breach[].\nLes torretes necessiten :beryllium: [accent]munició[]. onset.turretammo = Subministreu [accent]munició de beril·li[] a la torreta. onset.walls = Els [accent]murs[] poden evitar que el dany arribi a les estructures importants.\nConstruïu alguns :beryllium-wall: [accent]murs de beril·li[] al voltant de la torreta. onset.enemies = S’apropa un enemic. Prepareu la defensa. @@ -2447,11 +2447,11 @@ block.illuminator.description = Emet llum. block.message.description = Emmagatzema un missatge. Els aliats el poden fer servir per a comunicar-se. block.reinforced-message.description = Emmagatzema un missatge que serveix per a què els aliats es puguin comunicar. block.world-message.description = Un bloc de missatge que es far servir per a fer mapes. No es pot destruir. -block.world-processor.description = A processor for use in mapmaking. Cannot be destroyed. Capable of using privileged instructions, such as those that spawn units or change tiles. -block.world-cell.description = A memory cell for use in mapmaking. Cannot be destroyed. -block.world-switch.description = A switch block for use in mapmaking. Cannot be destroyed. Can only be interacted by players on the same team. -block.shield-projector.description = An unbreakable shield projector for use in mapmaking. -block.large-shield-projector.description = An unbreakable shield projector for use in mapmaking. +block.world-processor.description = Un processador que es fa servir per a cartografiar zones. No es pot destruir. Pot fer servir instruccions amb privilegis, com ara les que fan aparèixer unitats o canviar caselles. +block.world-cell.description = Un component de memòria que es fa servir per a cartografiar zones. No es pot destruir. +block.world-switch.description = Un bloc interruptor que es fa servir per a cartografiar zones. No es pot destruir. Només hi poden interactuar els jugadors del mateix equip. +block.shield-projector.description = Un projector d’escuts impenetrable que es fa servir per a cartografiar zones. +block.large-shield-projector.description = Un projector d’escuts impenetrable que es fa servir per a cartografiar zones. block.graphite-press.description = Comprimeix carbó fins obtenir-ne grafit. block.multi-press.description = Comprimeix carbó fins obtenir-ne grafit. Requereix refrigeració amb aigua. block.silicon-smelter.description = Refina silici a partir de sorra i carbó. @@ -2700,7 +2700,7 @@ block.unit-repair-tower.description = Repara totes les unitats a prop. Necessita block.radar.description = Escaneja el terreny gradualment i localitza unitats enemigues a gran distància. Necessita energia. block.shockwave-tower.description = Danya i destrueix projectils enemics dintre del seu abast. Requereix cianogen. block.canvas.description = Mostra una imatge senzilla amb una paleta predefinida. Es pot editar. -block.large-canvas.description = Displays a simple image with a pre-defined palette. Editable. +block.large-canvas.description = Mostra una imatge simple amb una paleta predefinida. Editable. unit.dagger.description = Dispara munició estàndard a tots els enemics propers. unit.mace.description = Dispara flames a tots els enemics propers. @@ -2775,7 +2775,7 @@ lst.radar = Localitza unitats al voltant d’un bloc amb l’abast indicat. lst.sensor = Obtén dades d’una estructura o unitat. lst.set = Estableix una variable. lst.operation = Executa una operació amb 1 o 2 variables. -lst.select = Conditionally set a variable to one of two values. +lst.select = Estableix una variable a un de dos valors segons una condició. lst.end = Salta al principi de la llista d’instruccions. lst.wait = Espera el nombre indicat de segons. lst.stop = Para l’execució d’aquest processador. @@ -2788,7 +2788,7 @@ lst.unitlocate = Localitza un tipus específic de posició/estructura en qualsev lst.getblock = Obtén les dades d’un bloc en qualsevol posició. lst.setblock = Estableix les dades d’un bloc en qualsevol posició. lst.spawnunit = Fes aparèixer una unitat en una posició. -lst.spawnbullet = Spawns a bullet at a location. +lst.spawnbullet = Fa aparèixer una bala en un lloc determinat. lst.applystatus = Aplica o esborra un efecte d’estat d’una unitat. lst.weathersense = Comprova si un tipus de temps meteorològics està actiu. lst.weatherset = Estableix l’estat actual per a un tipus de temps meteorològic. @@ -2807,11 +2807,11 @@ lst.setprop = Estableix una propietat d’una unitat o estructura. lst.effect = Crea un efecte de partícula. lst.sync = Sincronitza una variable a través de la xarxa.\nS’invoca com a molt 10 vegades per segon. lst.playsound = Reprodueix un so.\nEl volum i la panoramització (pan) poden ser un valor global o calculat segons la posició. -lst.playmusic = Plays a music track by name, e.g. [accent]"game1"[].\nIf [accent]interrupt[] is true, the previously playing music will be stopped.\nPlaying with null music and interrupt set to true will stop the current track.\nNote that music from data patches must be prefixed with [accent]dp-[]. +lst.playmusic = Reprodueix una peça musical per nom, com ara [accent]«game1»[].\nSi [accent]interrupt[] és cert, es pararà la peça anterior que s’estigui reproduint.\nSi la nova peça és null i s’executa l’ordre amb interrupt establert a cert, es pararà la peça actual.\nTingueu en compte que les dels pedaços de dades han d’anar precedides amb el prefix [accent]dp-[]. lst.makemarker = Crea una marca lògica al món.\nS’ha de donar un ID per a identificar-la.\nEs poden establir fins a 20.000 marcadors per món. lst.setmarker = Estableix una propietat per a la marca.\nL’ID que es faci servir ha de ser el mateix que el de la instrucció de crear la marca. lst.localeprint = Afegeix el valor d’una propietat de la traducció d’un mapa a la cua d’impressió.\nPer a establir paquets de traducció de mapes a l’editor de mapes, comproveu [accent]Informació del mapa > Paquets de traducció[].\nSi el client és un dispositiu mòbil, primer intenta imprimir una propietat que acabi en «.mobile». -lst.query = Queries units/buildings in a specified area.\nResults are outputted into [accent]@queries[].\nResults can be read from [accent]@queries[] using the [accent]Read[] instruction and an index.\nLength of [accent]@queries[] can be sensed using [accent]@size[] as a parameter. +lst.query = Cerca unitats/estrucutres en una àrea específica.\nEls resultats es desen a [accent]@queries[].\nEls resultats es poden llegir amb [accent]@queries[] i la instrucció i índex [accent]Read[].\nLa llargada de [accent]@queries[] es pot trobar amb [accent]@size[] com a paràmetre. lglobal.false = 0 lglobal.true = 1 @@ -2855,8 +2855,8 @@ lglobal.@clientUnit = Unitat del client que executa el codi lglobal.@clientName = Nom del jugador del client que executa el codi lglobal.@clientTeam = Identificador de l’equip que executa el codi lglobal.@clientMobile = Cert si el client que executa el codi és un dispositiu mòbil; fals altrament. -lglobal.@clientMusicPlaying = True if any music is currently playing -lglobal.@clientCurrentMusic = Name of the current track being played, or null +lglobal.@clientMusicPlaying = Cert si es reprodueix alguna peça musical. +lglobal.@clientCurrentMusic = Nom de la pista que es reprodueix actualment; o null altrament. lenum.type = Tipus del bloc/unitat.\nPer exemple, per a qualsevol encaminador, retornarà [accent]@router[].\nNo retorna una cadena de text. lenum.shoot = Dispara a una posició. @@ -3005,7 +3005,7 @@ lenum.unbind = Desactiva del tot el control lògic.\nContinua amb la IA estànda lenum.move = Mou a una posició exacta. lenum.approach = Aproxima a una zona determinada amb una posició i un radi. lenum.pathfind = Troba un camí i segueix una ruta fins al punt d’aparició d’enemics. -lenum.autopathfind = Busca un camí automàticament fins al nucli enemic més proper o punt d’aterratge.\nÉs el mateix que el camí d'una onada enemiga estàndard. +lenum.autopathfind = Busca un camí automàticament fins al nucli enemic més proper o punt d’aterratge.\nÉs el mateix que el camí d’una onada enemiga estàndard. lenum.target = Dispara a una posició. lenum.targetp = Dispara a un objectiu tenint en compte la seva velocitat a l’hora d’apuntar. lenum.itemdrop = Deixa un element. @@ -3022,7 +3022,7 @@ lenum.boost = Inicia/Detén el vol. lenum.flushtext = Passa el contingut de la cua d’impressió al marcador, si es pot.\nSi s’estableix «fetch» a vertader, s’intentarà carregar les propietats de la traducció del mapa o del joc. lenum.texture = Nom de la textura directa de l’atles de textures del joc (amb l’estil de noms kebab-case).\nSi «printFlush» s’estableix a vertader, consumeix el contingut de la cua d’impressió com a argument de text. -lenum.texturesize = Mida de la textura a les caselles. Un valor de zero indica que s’ha d'escalar l’amplada del marcador a la mida original de la textura. +lenum.texturesize = Mida de la textura a les caselles. Un valor de zero indica que s’ha d’escalar l’amplada del marcador a la mida original de la textura. lenum.autoscale = Indica si cal escalar el marcador segons el nivell de zoom del jugador. lenum.posi = Posició indexada que es fa servir per a marcadors de línia i de rectangles on l’índex zero és la primera posició. lenum.uvi = Posició de la textura que va de zero a u i que es fa servir per a marcadors de tipus rectangle. @@ -3043,9 +3043,9 @@ lenum.buildspeed = Multiplicador de la velocitat de construcció. lenum.unithealth = Unitats de salut amb les quals es comença. lenum.unitbuildspeed = Velocitat de construcció d’unitats a les fàbriques. lenum.unitcost = Multiplicador dels recursos necessaris per a construir les unitats. -lenum.unitdamage = Dany que inflingeixen les unitats. -lenum.blockhealth = Blocs de salot amb els quals es comença. -lenum.blockdamage = Dany que inflingeixen els blocs de dany (torretes). +lenum.unitdamage = Dany que infligeixen les unitats. +lenum.blockhealth = Blocs de salut amb els quals es comença. +lenum.blockdamage = Dany que infligeixen els blocs de dany (torretes). lenum.rtsminweight = Avantatge mínim que necessiten els esquadrons per a atacar. Com més alt sigui, més precabuts actuaran els esquadrons. lenum.rtsminsquad = Mida mínima dels esquadrons d’atac. lenum.maparea = Àrea jugable del mapa. No es podrà interactuar amb tot el que quedi a fora. @@ -3054,4 +3054,4 @@ lenum.solarmultiplier = Multiplicador de l’energia produïda pels panells sola lenum.dragmultiplier = Multiplicador d’arrossegament de l’entorn. lenum.ban = Prohibeix blocs o unitats. Aquestes no es podran situar ni construir. lenum.unban = Permet una unitat o bloc. -lenum.musicvolume = Global music volume multiplier.\nClamped to [0, 1]. +lenum.musicvolume = Multiplicador global del volum de música.\nLimitat a l’interval [0, 1]. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 16c944ad33..bd1fb93de8 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -319,8 +319,8 @@ server.admins.none = Žádní správci nebyli nalezeni. server.add = Přidat server server.delete = Jsi si jistý, že chceš smazat tento server? server.edit = Upravit server -server.outdated = [scarlet]Zastaralá verze serveru![] -server.outdated.client = [scarlet]Zastaralá verze klienta![] +server.outdated = [red]⚠ Zastaralá verze serveru![] +server.outdated.client = [red]⚠ Zastaralá verze klienta![] server.version = [gray]Verze: {0} {1}[] server.custombuild = [accent]Upravená verze hry[] confirmban = Jsi si jistý, že chceš zakázat hráči "{0}[white]" přístup na server?[] diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index ed577fdbdc..e9bb0590ff 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -319,8 +319,8 @@ server.admins.none = ¡No hay ningún administrador! server.add = Agregar servidor server.delete = ¿Realmente quieres borrar este servidor? server.edit = Editar Servidor -server.outdated = [scarlet]¡Servidor Desactualizado![] -server.outdated.client = [scarlet]¡Cliente Desactualizado![] +server.outdated = [red]⚠ ¡Servidor Desactualizado![] +server.outdated.client = [red]⚠ ¡Cliente Desactualizado![] server.version = [gray]v{0} {1} server.custombuild = [accent]Versión personalizada confirmban = ¿Quieres vetar a "{0}[white]"? diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 7e15e14029..8fa1d88598 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -319,8 +319,8 @@ server.admins.none = Walang nahnap na admin! server.add = Maglagay ng Server server.delete = Sigurado ka bang gusto mong tanggalin ang server na ito? server.edit = Ipalit ang Server -server.outdated = [scarlet]Di-bagong Server![] -server.outdated.client = [scarlet]Di-bagong Client![] +server.outdated = [red]⚠ Di-bagong Server![] +server.outdated.client = [red]⚠ Di-bagong Client![] server.version = [gray]v{0} {1} server.custombuild = [accent]Di-karaniwang Build confirmban = Sigurado ka bang gusto mong itanggal si "{0}[white]"? diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index e64dd3fde9..38df7fb19e 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -319,8 +319,8 @@ server.admins.none = Aucun administrateur trouvé ! server.add = Ajouter un serveur server.delete = Êtes-vous sûr de vouloir supprimer ce serveur ? server.edit = Modifier le serveur -server.outdated = [scarlet]Serveur obsolète ![] -server.outdated.client = [scarlet]Client obsolète ![] +server.outdated = [red]⚠ Serveur obsolète ![] +server.outdated.client = [red]⚠ Client obsolète ![] server.version = [gray]Version : {0} {1} server.custombuild = [accent]Version personnalisée confirmban = Êtes-vous sûr de vouloir bannir "{0}[white]" ? diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index fd0dd6c47d..c43fee4d2e 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -319,8 +319,8 @@ server.admins.none = Nem találhatók adminisztrátorok! server.add = Kiszolgáló hozzáadása server.delete = Biztosan törlöd ezt a kiszolgálót? server.edit = Kiszolgáló szerkesztése -server.outdated = [scarlet]Elavult kiszolgáló![] -server.outdated.client = [scarlet]Elavult kliens![] +server.outdated = [red]⚠ Elavult kiszolgáló![] +server.outdated.client = [red]⚠ Elavult kliens![] server.version = [gray]v{0} {1} server.custombuild = [accent]Egyéni összeállítás confirmban = Biztosan kitiltod a(z) „{0}[white]” nevű játékost? diff --git a/core/assets/bundles/bundle_id_ID.properties b/core/assets/bundles/bundle_id_ID.properties index 9ce38c6405..051bccf6ab 100644 --- a/core/assets/bundles/bundle_id_ID.properties +++ b/core/assets/bundles/bundle_id_ID.properties @@ -319,8 +319,8 @@ server.admins.none = Tidak ada admin ditemukan! server.add = Tambah Server server.delete = Anda yakin ingin menghapus server ini? server.edit = Sunting Server -server.outdated = [scarlet]Server Kedaluwarsa![] -server.outdated.client = [scarlet]Klien Kedaluwarsa![] +server.outdated = [red]⚠ Server Kedaluwarsa![] +server.outdated.client = [red]⚠ Klien Kedaluwarsa![] server.version = [gray]v{0} {1} server.custombuild = [accent]Bentuk Kustom confirmban = Anda yakin ingin melarang pemain ini untuk masuk lagi? diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index 747f136abb..c3ee2ce922 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -319,8 +319,8 @@ server.admins.none = Nessun amministratore trovato! server.add = Aggiungi Server server.delete = Sei sicuro di voler eliminare questo server? server.edit = Modifica Server -server.outdated = [scarlet]Server Obsoleto![] -server.outdated.client = [scarlet]Client Obsoleto![] +server.outdated = [red]⚠ Server Obsoleto![] +server.outdated.client = [red]⚠ Client Obsoleto![] server.version = [gray]v{0} {1} server.custombuild = [accent]Build Personalizzata confirmban = Sei sicuro di voler bandire "{0}[white]"? diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index 778de3631e..a16c9aa84a 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -187,7 +187,7 @@ mod.incompletedependencies.details = 依存関係が無効または欠損して mod.requiresversion = ゲームのバージョンが必要です: [red]{0} mod.errors = コンテンツの読み込み中にエラーが発生しました。 -mod.errors.map = [scarlet]Errors have occurred loading map content. All custom content has been disabled.[]\nThis map will likely not function correctly. +mod.errors.map = [scarlet]マップコンテンツの読み込み中にエラーが発生しました。すべてのカスタムコンテンツは無効化されています。[]\nこのマップは正しく機能しない可能性があります。 mod.noerrorplay = [scarlet]以下のModにエラーがあります。[] Modを無効化するか、エラーを修正してください。 mod.enable = 有効化 mod.requiresrestart = このModをインストールするためにはゲームの再起動が必要です。 @@ -285,7 +285,7 @@ servers.remote = リモートサーバー servers.global = コミュニティサーバー servers.disclaimer = コミュニティサーバーは、[accent]開発者が所有、管理していません。[] \n\nサーバーには全ての年齢層に適さないコンテンツが存在する可能性があります。 -servers.connect.confirm = Join the following server?\n\n[accent]{0}[]\n\nOnly connect to servers you trust! +servers.connect.confirm = このサーバーに参加しますか?\n\n[accent]{0}[]\n\n信用できるサーバーにのみ接続するようにしてください! servers.showhidden = 非表示のサーバーを表示する server.shown = 表示 server.hidden = 非表示 @@ -319,8 +319,8 @@ server.admins.none = 管理者はいません! server.add = サーバーを追加 server.delete = サーバーを削除してもよろしいですか? server.edit = サーバーを編集 -server.outdated = [crimson]古いサーバーです![] -server.outdated.client = [crimson]古いクライアントです![] +server.outdated = [red]⚠ 古いサーバーです![] +server.outdated.client = [red]⚠ 古いクライアントです![] server.version = [lightgray]バージョン: {0} {1} server.custombuild = [accent]カスタムビルド confirmban = {0} をBanしてもよろしいですか? @@ -524,7 +524,7 @@ editor.filters.description = 説明 editor.filters.modname = Modの名前 editor.filters.prioritizemod = Modの優先順位 editor.filters.prioritizecustom = カスタムの優先度 -editor.filters.planetselect = Planet Selection +editor.filters.planetselect = 惑星の選択 editor.shiftx = Shift X editor.shifty = Shift Y @@ -576,7 +576,7 @@ asset.content.badname = [red]無効な名前です!ほとんどの符号は許 asset.content.import.file = ファイルをインポート asset.content.import.clipboard = クリップボードからインポート asset.exists = そのタイプのアセットで、同じパス/名前のものが既に存在します:\n[lightgray]{0} -asset.exists.audio = A sound/music asset with that name is already loaded:\n[lightgray]{0} +asset.exists.audio = その名前の音声/音楽アセットは同じ名前のものが既に存在します:\n[lightgray]{0} asset.broken = アセットファイルが見つかりませんでした。\nデータの不適切なインポート、または正しくエクスポートされていない保存ファイルが原因である可能性があります。 asset.audio.invalid = 音声ファイルを読み込めませんでした。\n有効な OGG または MP3 ファイルであることを確認してください。 asset.delete.confirm = このアセットを削除してもよろしいですか? @@ -1440,10 +1440,10 @@ setting.milliseconds = {0} ミリ秒 setting.fullscreen.name = フルスクリーン setting.fps.name = FPSを表示 setting.console.name = コンソールを有効にする -setting.drawhitboxes.name = Display Hitboxes -setting.showperformance.name = Display Performance Metrics -setting.buildingtimestep.name = Fixed Building Timestep -setting.buildingtimestep.description = Runs buildings on a fixed timestep with constant delta,\nas opposed to every frame with variable delta.\nIf buildings cannot be updated fast enough, the game will slow down.\nIf you don't know what that means, don't touch this option. +setting.drawhitboxes.name = 当たり判定を表示する +setting.showperformance.name = パフォーマンスを表示する +setting.buildingtimestep.name = 固定建築更新間隔 +setting.buildingtimestep.description = 建築物を一定のデルタ時間で固定された更新間隔で動作させます。\nフレームごとの可変デルタ時間での更新とは異なります。\n建築物を十分な速度で更新できない場合、ゲームの進行が遅くなります。\nこの説明の意味が分からない場合は、この設定を変更しないでください。 setting.smoothcamera.name = スムーズなカメラ setting.detach-camera.name = フリーカメラ setting.detach-camera.description = 有効にすると、カメラがプレイヤーユニットから独立して移動します。\nこの設定はホットキーに割り当て可能です。 @@ -1586,9 +1586,9 @@ keybind.chat_mode.name = チャットモードの変更 keybind.drop_unit.name = ユニット投下 keybind.zoom_minimap.name = ミニマップのズーム keybind.detach_camera.name = フリーカメラの切り替え -keybind.teleport_cursor.name = Teleport To Cursor (Sandbox) -keybind.debug_hitboxes.name = デバッグヒットボックスの切り替え -keybind.performance_metrics.name = Toggle Performance Metrics +keybind.teleport_cursor.name = カーソル位置にテレポート (Sandbox) +keybind.debug_hitboxes.name = 当たり判定のデバッグ切り替え +keybind.performance_metrics.name = パフォーマンス表示の切り替え mode.help.title = モード説明 mode.survival.name = サバイバル mode.survival.description = 通常のモードです。資源も限られる中、自動的にウェーブが進行していきます。\n[gray]マップに敵の出現設定が必要です。 @@ -1646,7 +1646,7 @@ rules.enemyCheat = 敵(赤チーム)の資源の無限化 rules.blockhealthmultiplier = ブロック体力倍率 rules.blockdamagemultiplier = ブロックダメージ倍率 rules.unitfactoryactivation = ユニット工場の起動遅延 -rules.unitfactoryactivation.objective = Unit Factories Activating: {0} +rules.unitfactoryactivation.objective = ユニット工場の有効化: {0} rules.unitbuildspeedmultiplier = ユニット製造速度倍率 rules.unitcostmultiplier = ユニット製造コスト倍率 rules.unithealthmultiplier = ユニット体力倍率 @@ -1682,22 +1682,22 @@ rules.title.experimental = 実験的なゲームプレイ rules.title.environment = 環境 rules.title.teams = チーム rules.title.planet = 惑星 -rules.title.music = Music -rules.alwaysplaymusic = Always Play Music -rules.alwaysplaymusic.info = Overrides the 'always play music' audio setting. -rules.ambientmusic = Ambient Music -rules.ambientmusic.info = Comma-separated music tracks that randomly play under normal circumstances.\nIf empty, default music will be used. -rules.darkmusic = Conflict Music -rules.darkmusic.info = Comma-separated music tracks that are more likely to play during battle.\nIf empty, default music will be used. -rules.nomusic = Disable Automatic Music -rules.nomusic.info = Turns off all automatic music playback.\nUseful for maps that have custom music played using processors. +rules.title.music = 音楽 +rules.alwaysplaymusic = 常に音楽を再生する +rules.alwaysplaymusic.info = '常に音楽を再生する'音楽設定を上書きします。 +rules.ambientmusic = 環境音楽 +rules.ambientmusic.info = 通常時にランダム再生される音楽トラック(カンマ区切り)。\n空欄の場合はデフォルトの音楽が使用されます。 +rules.darkmusic = 戦闘音楽 +rules.darkmusic.info = 戦闘中に再生されやすい音楽トラック(カンマ区切り)。\n空欄の場合はデフォルトの音楽が使用されます。 +rules.nomusic = 自動音楽再生を無効化する +rules.nomusic.info = すべての自動音楽再生を無効にします。\nプロセッサでカスタム音楽を再生するマップに便利です。 rules.lighting = 霧 rules.fog = 戦場の霧 rules.invasions = 敵セクター侵攻 rules.legacylaunchpads = 旧発射台仕様 rules.legacylaunchpads.info = 着陸台なしで発射台を使用できるようにします(v7.0仕様)。 landingpad.legacy.disabled = [scarlet]\ue815 無効[lightgray](旧発射台は有効) -rules.hidespawns = Hide Enemy Spawns +rules.hidespawns = 敵の出現地点を非表示にする rules.randomwaveai = 不規則なウェーブAI rules.pauseDisabled = 一時停止の無効化 rules.fire = 火災 @@ -2447,11 +2447,11 @@ block.illuminator.description = 電力が必要な光源です。 block.message.description = メッセージを保存し、仲間間の通信に使用します。 block.reinforced-message.description = メッセージを保存し、仲間間の通信に使用します。 block.world-message.description = マップ作成時に使用するメッセージブロックです。破壊することはできません。 -block.world-processor.description = A processor for use in mapmaking. Cannot be destroyed. Capable of using privileged instructions, such as those that spawn units or change tiles. -block.world-cell.description = A memory cell for use in mapmaking. Cannot be destroyed. -block.world-switch.description = A switch block for use in mapmaking. Cannot be destroyed. Can only be interacted by players on the same team. -block.shield-projector.description = An unbreakable shield projector for use in mapmaking. -block.large-shield-projector.description = An unbreakable shield projector for use in mapmaking. +block.world-processor.description = マップ作成に使用するプロセッサーです。破壊できません。ユニットの生成やタイルの変更など、特権命令を使用できます。 +block.world-cell.description = マップ作成に使用するメモリセルです。破壊できません。 +block.world-switch.description = マップ作成に使用するスイッチブロックです。破壊できません。同じチームのプレイヤーのみ操作できます。 +block.shield-projector.description = マップ作成に使用する破壊不可能なシールドプロジェクターです。 +block.large-shield-projector.description = マップ作成に使用する破壊不可能な大型シールドプロジェクターです。 block.graphite-press.description = 石炭を圧縮し、黒鉛を生成します。 block.multi-press.description = 黒鉛圧縮機のアップグレード版です。水と電力を使用して、より効率的に石炭を圧縮します。 block.silicon-smelter.description = 石炭と砂からシリコンを製造します。 @@ -2775,7 +2775,7 @@ lst.radar = 射程内のユニットを探します。 lst.sensor = 建物やユニットのデータを取得します。 lst.set = 変数を定義し、数値を代入します。 lst.operation = 一つから二つの変数を使って演算をします。 -lst.select = Conditionally set a variable to one of two values. +lst.select = 条件に応じて、2つの値のうち1つを変数に設定します。 lst.end = 命令スタックの先頭にジャンプします。 lst.wait = 一定の秒数待ちます。 lst.stop = このプロセッサの実行を停止します。 @@ -2788,7 +2788,7 @@ lst.unitlocate = マップ上の任意の座標にある、特定のタイプの lst.getblock = 任意の座標のタイルの情報を取得します。 lst.setblock = 任意の座標のタイルの情報を変更します。 lst.spawnunit = 任意の座標にユニットをスポーンさせます。 -lst.spawnbullet = Spawns a bullet at a location. +lst.spawnbullet = 指定された位置に弾丸を生成します。 lst.applystatus = ユニットからステータス効果を適用または削除する。 lst.weathersense = 天候の種類が有効かどうかを確認します。 lst.weatherset = 天候の種類の現在の状態を設定します。 @@ -2855,8 +2855,8 @@ lglobal.@clientUnit = クライアントが操作しているユニット lglobal.@clientName = クライアントのプレイヤー名 lglobal.@clientTeam = クライアントのチームID lglobal.@clientMobile = クライアントがモバイルなら true、それ以外は false -lglobal.@clientMusicPlaying = True if any music is currently playing -lglobal.@clientCurrentMusic = Name of the current track being played, or null +lglobal.@clientMusicPlaying = 現在何らかの音楽が再生されている場合はtrue +lglobal.@clientCurrentMusic = 現在再生中のトラック名。再生されていない場合はnull lenum.type = ユニットや建物の種類を取得します。\n例: 任意のルーターに対して、 [accent]@router[] を返します。\n文字列ではありません。 lenum.shoot = 指定した座標に向かって撃ちます。 @@ -3054,4 +3054,4 @@ lenum.solarmultiplier = ソーラーパネルの発電量倍率。 lenum.dragmultiplier = 環境による抵抗倍率。 lenum.ban = 配置または建造できないブロックやユニット。 lenum.unban = ユニットまたはブロックの禁止を解除する。 -lenum.musicvolume = Global music volume multiplier.\nClamped to [0, 1]. +lenum.musicvolume = 音楽の音量倍率。\n [0, 1]の範囲に制限されます。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index a5cd8acbe6..291767570b 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -319,8 +319,8 @@ server.admins.none = 관리자를 찾을 수 없습니다! server.add = 서버 추가 server.delete = 정말로 이 서버를 삭제하시겠습니까? server.edit = 서버 편집 -server.outdated = [scarlet]서버 버전이 낮습니다![] -server.outdated.client = [scarlet]클라이언트 버전이 낮습니다![] +server.outdated = [red]⚠ 서버 버전이 낮습니다![] +server.outdated.client = [red]⚠ 클라이언트 버전이 낮습니다![] server.version = [gray]v{0} {1} server.custombuild = [accent]사용자 정의 서버 confirmban = 정말로 "{0}[white]" 을(를) 차단하시겠습니까? diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 62d50d68f5..dac2406eda 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -319,8 +319,8 @@ server.admins.none = Nenhum administrador encontrado! server.add = Adicionar servidor server.delete = Você tem certeza que quer deletar esse servidor? server.edit = Editar Servidor -server.outdated = [scarlet]Servidor Desatualizado![] -server.outdated.client = [scarlet]Cliente Desatualizado![] +server.outdated = [red]⚠ Servidor Desatualizado![] +server.outdated.client = [red]⚠ Cliente Desatualizado![] server.version = [gray]v{0} {1} server.custombuild = [accent]Versão Customizada confirmban = Você tem certeza que quer banir "{0}[white]"? diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index fb749d438f..42acc18915 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -319,8 +319,8 @@ server.admins.none = Nu s-au găsit admini! server.add = Adăugă server server.delete = Sigur vrei să ștergi acest server? server.edit = Editează Server -server.outdated = [scarlet]Server Învechit![] -server.outdated.client = [scarlet]Client Învechit![] +server.outdated = [red]⚠ Server Învechit![] +server.outdated.client = [red]⚠ Client Învechit![] server.version = [gray]v{0} {1} server.custombuild = [accent]Build Personalizat confirmban = Sigur vrei să interzici jucătorul "{0}[white]"? diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index dc769999d3..a920318399 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -319,8 +319,8 @@ server.admins.none = Администраторов нет! server.add = Добавить сервер server.delete = Вы действительно хотите удалить этот сервер? server.edit = Редактировать сервер -server.outdated = [scarlet]Устаревший сервер![] -server.outdated.client = [scarlet]Устаревший клиент![] +server.outdated = [red]⚠ Устаревший сервер![] +server.outdated.client = [red]⚠ Устаревший клиент![] server.version = [gray]Версия: {0} {1} server.custombuild = [accent]Пользовательская сборка confirmban = Вы действительно хотите заблокировать игрока «{0}[white]»? diff --git a/core/assets/bundles/bundle_sr.properties b/core/assets/bundles/bundle_sr.properties index 28f0dc7906..be24eb758b 100644 --- a/core/assets/bundles/bundle_sr.properties +++ b/core/assets/bundles/bundle_sr.properties @@ -319,8 +319,8 @@ server.admins.none = Nema pronađenih administratora! server.add = Dodaj Server server.delete = Da li želite da obrišete ovaj server? server.edit = Izmeni Server -server.outdated = [scarlet]Zastareli Server![] -server.outdated.client = [scarlet]Zastareli Klijent![] +server.outdated = [red]⚠ Zastareli Server![] +server.outdated.client = [red]⚠ Zastareli Klijent![] server.version = [gray]v{0} {1} server.custombuild = [accent]Svjojehodna Verzija confirmban = Da li ste sigurni da želite da [scarlet]trajno[] izbacite "{0}[white]"? diff --git a/core/assets/bundles/bundle_vi.properties b/core/assets/bundles/bundle_vi.properties index 3a69136789..e6525a725c 100644 --- a/core/assets/bundles/bundle_vi.properties +++ b/core/assets/bundles/bundle_vi.properties @@ -154,10 +154,10 @@ mods.update.available = [lightgray]Cập Nhật Có Sẵn: {0} mods.update.all = Cập Nhật Tất Cả Bản Mod mods.downloading = [accent]Đang Tải Xuống Mod: {0} -mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer. -mod.warning.read = I actually read this -mod.warning.accept = I accept the responsibility -mod.warning.deny = Disable my mods +mod.warning = ⚠ Các bản mod có thể thực thi mã tùy ý.\n\nKhông bao giờ tải mod từ nguồn bạn không tin tưởng - họ có thể ăn cắp dữ liệu hoặc gây hại đến máy tính của bạn. +mod.warning.read = Tôi thực sự đã đọc diều này +mod.warning.accept = Tôi nhận trách nhiệm +mod.warning.deny = Tắt các mod của tôi mod.installed = [[Đã cài đặt] mod.display = [gray]Mod:[accent] {0} mod.enabled = [lightgray]Đã bật @@ -319,8 +319,8 @@ server.admins.none = Không tìm thấy quản trị viên nào! server.add = Thêm máy chủ server.delete = Bạn có chắc chắn muốn xóa máy chủ này không? server.edit = Chỉnh sửa máy chủ -server.outdated = [scarlet]Máy chủ lỗi thời![] -server.outdated.client = [scarlet]Máy khách lỗi thời![] +server.outdated = [red]⚠ Máy chủ lỗi thời![] +server.outdated.client = [red]⚠ Máy khách lỗi thời![] server.version = [gray]v{0} {1} server.custombuild = [accent]Bản dựng tùy chỉnh confirmban = Bạn có chắc chắn muốn cấm "{0}[white]"? diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index e53db10968..87fbfc3117 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -319,8 +319,8 @@ server.admins.none = 找不到管理员! server.add = 添加服务器 server.delete = 确定要删除这个服务器吗? server.edit = 编辑服务器 -server.outdated = [scarlet]服务器版本过低![] -server.outdated.client = [scarlet]客户端版本过低![] +server.outdated = [red]⚠ 服务器版本过低![] +server.outdated.client = [red]⚠ 客户端版本过低![] server.version = [gray]v{0} {1} server.custombuild = [accent]自行编译 confirmban = 确定要封禁“{0}[white]”吗? diff --git a/core/assets/scripts/global.js b/core/assets/scripts/global.js index 6ac529fb87..dffdd76ca1 100755 --- a/core/assets/scripts/global.js +++ b/core/assets/scripts/global.js @@ -48,15 +48,27 @@ const Arrays = Packages.java.util.Arrays const TextureFilter = Packages.arc.graphics.TextureFilter const TextureWrap = Packages.arc.graphics.TextureWrap importPackage(Packages.arc) +importPackage(Packages.arc.assets) +importPackage(Packages.arc.assets.loaders) importPackage(Packages.arc.audio) +importPackage(Packages.arc.backend.headless) importPackage(Packages.arc.files) importPackage(Packages.arc.flabel) +importPackage(Packages.arc.freetype) importPackage(Packages.arc.func) +importPackage(Packages.arc.fx) +importPackage(Packages.arc.fx.filters) +importPackage(Packages.arc.fx.util) importPackage(Packages.arc.graphics) +importPackage(Packages.arc.graphics.g3d) importPackage(Packages.arc.graphics.g2d) importPackage(Packages.arc.input) importPackage(Packages.arc.math) importPackage(Packages.arc.math.geom) +importPackage(Packages.arc.mock) +importPackage(Packages.arc.net) +importPackage(Packages.arc.net.dns) +importPackage(Packages.arc.packer) importPackage(Packages.arc.scene) importPackage(Packages.arc.scene.actions) importPackage(Packages.arc.scene.event) @@ -194,6 +206,7 @@ const LaunchItemEvent = Packages.mindustry.game.EventType.LaunchItemEvent const SectorInvasionEvent = Packages.mindustry.game.EventType.SectorInvasionEvent const SectorLoseEvent = Packages.mindustry.game.EventType.SectorLoseEvent const SaveLoadEvent = Packages.mindustry.game.EventType.SaveLoadEvent +const TextureStreamEvent = Packages.mindustry.game.EventType.TextureStreamEvent const DataPatchLoadEvent = Packages.mindustry.game.EventType.DataPatchLoadEvent const WorldLoadEndEvent = Packages.mindustry.game.EventType.WorldLoadEndEvent const WorldLoadBeginEvent = Packages.mindustry.game.EventType.WorldLoadBeginEvent diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 53b4f6fee8..1bf6751b42 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -1933,7 +1933,7 @@ public class Fx{ float angle = rand.random(360f); float lenRand = rand.random(0.5f, 1.2f); Tmp.v1.trns(angle, circleRad); - + for(int s : Mathf.signs){ Drawf.tri(e.x + Tmp.v1.x, e.y + Tmp.v1.y, e.fout() * 10f, e.fout() * 10f * lenRand + 8f, angle + 90f + s * 90f); } @@ -2807,12 +2807,7 @@ public class Fx{ shieldBreak = new Effect(40, e -> { color(e.color); stroke(3f * e.fout()); - if(e.data instanceof ForceFieldAbility ab){ - Lines.poly(e.x, e.y, ab.sides, e.rotation + e.fin(), ab.rotation); - return; - } - - Lines.poly(e.x, e.y, 6, e.rotation + e.fin()); + Lines.poly(e.x, e.y, e.data instanceof Integer i ? i : 6, e.rotation + e.fin()); }).followParent(true), arcShieldBreak = new Effect(40, e -> { diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 99b7ca9f9d..1da6632cd3 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -164,6 +164,16 @@ public class NetClient implements ApplicationListener{ Call.requestAssets(missing.toArray()); }); + net.handleClient(TextureStream.class, data -> { + try(DataInputStream in = new DataInputStream(data.stream)){ + String name = in.readUTF(); + byte[] pngData = in.readAllBytes(); + if(!headless) state.data.addTexture(name, pngData); + }catch(IOException e){ + Log.err("Failed to read server texture stream", e); + } + }); + net.handleClient(StreamBegin.class, data -> { boolean isWorld = data.type == Net.packetIdWorldStream, isAssets = data.type == Net.packetIdAssetStream; diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 9956548d63..25ba0ae1e1 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -552,6 +552,38 @@ public class NetServer implements ApplicationListener{ debug("Packed @ of world data to @ (@ / @)", Strings.formatByteCount(stream.size()), player.name, player.con.address, player.uuid()); } + /** + * Streams a texture to a single connected client. This may take some time if the image is large or if the connection is poor. + * Make sure to call {@link mindustry.mod.DataManager#removeTexture} when the image is no longer needed to prevent resource leaks. + * Use {@link PixmapIO#writePngBytes} to get Pixmap bytes. Respect {@link mindustry.mod.DataPatcher#maxImageSize}. */ + public void sendTexture(NetConnection con, String name, byte[] pngData){ + mainExecutor.submit(() -> { + var stream = packTexture(name, pngData); + con.sendStreamAsync(new TextureStream(), stream); + }); + } + + /** Streams a texture to every connected client. See {@link #sendTexture(NetConnection, String, byte[])} for more info. */ + public void sendTexture(String name, byte[] pngData){ + mainExecutor.submit(() -> { + var stream = packTexture(name, pngData); + for(NetConnection con : net.getConnections()){ + con.sendStreamAsync(new TextureStream(), stream); + } + }); + } + + private ByteArrayOutputStream packTexture(String name, byte[] pngData){ + var stream = new ByteArrayOutputStream(); + try(DataOutputStream out = new DataOutputStream(stream)){ + out.writeUTF(name); + out.write(pngData); + }catch(IOException e){ + throw new RuntimeException(e); + } + return stream; + } + public void addPacketHandler(String type, Cons2 handler){ customPacketHandlers.get(type, Seq::new).add(handler); } diff --git a/core/src/mindustry/core/UI.java b/core/src/mindustry/core/UI.java index 70666c8f20..381aa9703f 100644 --- a/core/src/mindustry/core/UI.java +++ b/core/src/mindustry/core/UI.java @@ -329,9 +329,11 @@ public class UI implements ApplicationListener, Loadable{ }}); }else{ new Dialog(titleText){{ - cont.margin(30).add(text).padRight(6f); + cont.image().width(400f).pad(2).height(4f).color(Pal.accent); + cont.row(); + cont.add(text).row(); TextFieldFilter filter = numbers ? TextFieldFilter.digitsOnly : (f, c) -> true; - TextField field = cont.field(def, t -> {}).size(330f, 50f).get(); + TextField field = cont.field(def, t -> {}).size(400f, 50f).get(); field.setMaxLength(textLength); field.setFilter(filter); buttons.defaults().size(120, 54).pad(4); diff --git a/core/src/mindustry/entities/abilities/ForceFieldAbility.java b/core/src/mindustry/entities/abilities/ForceFieldAbility.java index fb9dfb0935..2ee0b0d3d6 100644 --- a/core/src/mindustry/entities/abilities/ForceFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ForceFieldAbility.java @@ -91,7 +91,7 @@ public class ForceFieldAbility extends Ability{ if(unit.shield <= 0f && !wasBroken){ unit.shield -= cooldown * regen; - Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), this); + Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), sides); breakSound.at(unit.x, unit.y); } @@ -120,7 +120,7 @@ public class ForceFieldAbility extends Ability{ //self-destructing units can have a shield on death if(unit.shield > 0f && !wasBroken){ - Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), this); + Fx.shieldBreak.at(unit.x, unit.y, radius, unit.type.shieldColor(unit), sides); breakSound.at(unit.x, unit.y); } } diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index a0b96124fc..b10d39748f 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -119,6 +119,15 @@ public class EventType{ } } + /** Called when a new texture is received from the server via {@link mindustry.core.NetServer#sendTexture}. */ + public static class TextureStreamEvent { + public final String name; + + public TextureStreamEvent(String name){ + this.name = name; + } + } + public static class SaveLoadEvent{ public final boolean isMap; diff --git a/core/src/mindustry/input/Placement.java b/core/src/mindustry/input/Placement.java index f50f33d9db..95bc50cfda 100644 --- a/core/src/mindustry/input/Placement.java +++ b/core/src/mindustry/input/Placement.java @@ -10,8 +10,67 @@ import mindustry.entities.units.*; import mindustry.world.*; import mindustry.world.blocks.distribution.*; +import java.util.*; + import static mindustry.Vars.*; +interface BridgePlacer{ + boolean unlockedNow(); + + boolean positionsValid(int x1, int y1, int x2, int y2); + + void applyToPlans(BuildPlan cur, BuildPlan other); +} + +class ItemBridgePlacer implements BridgePlacer{ + private final ItemBridge bridge; + + ItemBridgePlacer(ItemBridge bridge){ + this.bridge = bridge; + } + + @Override + public boolean unlockedNow(){ + return bridge.unlockedNow(); + } + + @Override + public boolean positionsValid(int x1, int y1, int x2, int y2){ + return bridge.positionsValid(x1, y1, x2, y2); + } + + @Override + public void applyToPlans(BuildPlan cur, BuildPlan other){ + cur.block = bridge; + other.block = bridge; + other.config = new Point2(cur.x - other.x, cur.y - other.y); + } +} + +class DirectionBridgePlacer implements BridgePlacer{ + private final DirectionBridge bridge; + + DirectionBridgePlacer(DirectionBridge bridge){ + this.bridge = bridge; + } + + @Override + public boolean unlockedNow(){ + return bridge.unlockedNow(); + } + + @Override + public boolean positionsValid(int x1, int y1, int x2, int y2){ + return bridge.positionsValid(x1, y1, x2, y2); + } + + @Override + public void applyToPlans(BuildPlan cur, BuildPlan other){ + cur.block = bridge; + other.block = bridge; + } +} + public class Placement{ private static final Seq plans1 = new Seq<>(); private static final Seq tmpPoints = new Seq<>(), tmpPoints2 = new Seq<>(); @@ -19,6 +78,7 @@ public class Placement{ private static final NormalizeDrawResult drawResult = new NormalizeDrawResult(); private static final Bresenham2 bres = new Bresenham2(); private static final Seq points = new Seq<>(); + private static final IntSeq tmpInts = new IntSeq(), tmpInts2 = new IntSeq(); //for pathfinding private static final IntFloatMap costs = new IntFloatMap(); @@ -97,7 +157,7 @@ public class Placement{ boolean addedLast = false; outer: - for(int i = 0; i < base.size;){ + for(int i = 0; i < base.size; ){ var point = base.get(i); result.add(point); if(i == base.size - 1) addedLast = true; @@ -115,7 +175,7 @@ public class Placement{ } //if it got here, that means nothing was found. try to proceed to the next node anyway - i ++; + i++; } if(!addedLast && !base.isEmpty()) result.add(base.peek()); @@ -132,7 +192,8 @@ public class Placement{ calculateBridges(plans, bridge, false, t -> false); } - public static void calculateBridges(Seq plans, ItemBridge bridge, boolean hasJunction, Boolf avoid){ + private static void calculateBridges(Seq plans, BridgePlacer bridge, boolean hasJunction, Boolf avoid){ + //common checks if(isSidePlace(plans) || plans.size == 0) return; //check for orthogonal placement + unlocked state @@ -140,139 +201,119 @@ public class Placement{ return; } + smartCalculateBridges(plans, bridge, hasJunction, avoid); + } + + private static void smartCalculateBridges(Seq plans, BridgePlacer bridge, boolean hasJunction, Boolf avoid){ Boolf placeable = plan -> - (plan.placeable(player.team()) || (plan.tile() != null && plan.tile().block() == plan.block)) && //don't count the same block as inaccessible - !(plan != plans.first() && plan.build() != null && plan.build().rotation != plan.rotation && avoid.get(plan.tile().block())); + (plan.placeable(player.team()) || (plan.tile() != null && plan.tile().block() == plan.block)) && //don't count the same block as inaccessible + !(plan != plans.first() && plan.build() != null && plan.build().rotation != plan.rotation && avoid.get(plan.tile().block())); var result = plans1.clear(); - var rotated = plans.first().tile() != null && plans.first().tile().absoluteRelativeTo(plans.peek().x, plans.peek().y) == Mathf.mod(plans.first().rotation + 2, 4); - outer: - for(int i = 0; i < plans.size;){ + // Use DP for smarter bridge placement + final int conveyorCost = 3; + final int junctionCost = 30; + final int bridgeCost = 200; + final int bridgeOverEmptyPenalty = 5; + final int infCost = Integer.MAX_VALUE / 2; // Avoid overflow when adding + + int N = plans.size; + var dp = tmpInts.setSize(2 * N); + var parent = tmpInts2.setSize(2 * N); + Arrays.fill(dp, 0, 2 * N, infCost); + Arrays.fill(parent, 0, 2 * N, -1); + dp[0] = 0; + dp[N] = bridgeCost; + + for(int i = 1; i < N; i++){ var cur = plans.get(i); - result.add(cur); + boolean canPlace = placeable.get(cur); + boolean needJunction = hasJunction && (cur.tile() == null || avoid.get(cur.tile().block())); - //gap found - if(i < plans.size - 1 && placeable.get(cur) && !placeable.get(plans.get(i + 1))){ - boolean wereSame = true; + if(!canPlace && !needJunction){ + continue; + } - //find the closest valid position within range - for(int j = i + 1; j < plans.size; j++){ - var other = plans.get(j); - - //out of range now, set to current position and keep scanning forward for next occurrence - if(!bridge.positionsValid(cur.x, cur.y, other.x, other.y)){ - //add 'missed' conveyors - for(int k = i + 1; k < j; k++){ - result.add(plans.get(k)); - } - i = j; - continue outer; - }else if(placeable.get(other)){ - - if(wereSame && hasJunction){ - //the gap is fake, it's just conveyors that can be replaced with junctions - i ++; - continue outer; - }else{ - //found a link, assign bridges - cur.block = bridge; - other.block = bridge; - if(rotated){ - other.config = new Point2(cur.x - other.x, cur.y - other.y); - }else{ - cur.config = new Point2(other.x - cur.x, other.y - cur.y); - } - - i = j; - continue outer; - } - } - - if(other.tile() != null && !avoid.get(other.tile().block())){ - wereSame = false; - } - } - - //if it got here, that means nothing was found. this likely means there's a bunch of stuff at the end; add it and bail out - for(int j = i + 1; j < plans.size; j++){ - result.add(plans.get(j)); - } - break; + if(canPlace){ + dp[i] = dp[i - 1] + conveyorCost; }else{ - i ++; + dp[i] = dp[i - 1] + junctionCost; + } + parent[i] = i - 1; + + if(dp[i] < infCost && canPlace){ + dp[N + i] = dp[i] + bridgeCost; + parent[N + i] = i - 1; + } + + // Consider bridges from all previous positions + if(i >= 2 && canPlace){ + int emptyPenalty = 0; + if(placeable.get(plans.get(i - 1))){ + emptyPenalty += bridgeOverEmptyPenalty; + } + + for(int j = i - 2; j >= 0; j--){ + var other = plans.get(j); + if(!bridge.positionsValid(cur.x, cur.y, other.x, other.y)){ + break; // No need to check further back if this one is out of range + } + + if(placeable.get(other)){ + int cost = dp[N + j] + bridgeCost + emptyPenalty; + if(dp[N + i] > cost){ + dp[N + i] = cost; + parent[N + i] = j; + } + emptyPenalty += bridgeOverEmptyPenalty; + } + } + } + + if(dp[N + i] < dp[i]){ + dp[i] = dp[N + i]; + parent[i] = parent[N + i]; + } + + if(canPlace && dp[i] >= infCost){ + // Unable to connect, restart a new segment + dp[i] = 0; + dp[N + i] = bridgeCost; } } + // Backtrack to assign bridges + int bridgeMode = 0; + for(int i = N - 1; i >= 0; ){ + var cur = plans.get(i); + int p = parent[bridgeMode + i]; + + if(p == -1 || p == i - 1){ + // No connection, connected by conveyor, or junction, no bridge needed + result.add(cur); + bridgeMode = 0; + i--; + }else{ + // Connected by bridge, assign it + var other = plans.get(p); + bridge.applyToPlans(cur, other); + result.add(cur); + i = p; + bridgeMode = N; + } + } + + result.reverse(); plans.set(result); } + public static void calculateBridges(Seq plans, ItemBridge bridge, boolean hasJunction, Boolf avoid){ + calculateBridges(plans, new ItemBridgePlacer(bridge), hasJunction, avoid); + } + public static void calculateBridges(Seq plans, DirectionBridge bridge, boolean hasJunction, Boolf avoid){ - if(isSidePlace(plans) || plans.size == 0) return; - - //check for orthogonal placement + unlocked state - if(!(plans.first().x == plans.peek().x || plans.first().y == plans.peek().y) || !bridge.unlockedNow()){ - return; - } - - Boolf placeable = plan -> - (plan.placeable(player.team()) || (plan.tile() != null && plan.tile().block() == plan.block)) && //don't count the same block as inaccessible - !(plan != plans.first() && plan.build() != null && plan.build().rotation != plan.rotation && avoid.get(plan.tile().block())); - - var result = plans1.clear(); - - outer: - for(int i = 0; i < plans.size;){ - var cur = plans.get(i); - result.add(cur); - - //gap found - if(i < plans.size - 1 && placeable.get(cur) && !placeable.get(plans.get(i + 1))){ - boolean wereSame = true; - - //find the closest valid position within range - for(int j = i + 1; j < plans.size; j++){ - var other = plans.get(j); - - //out of range now, set to current position and keep scanning forward for next occurrence - if(!bridge.positionsValid(cur.x, cur.y, other.x, other.y)){ - //add 'missed' conveyors - for(int k = i + 1; k < j; k++){ - result.add(plans.get(k)); - } - i = j; - continue outer; - }else if(placeable.get(other)){ - - if(wereSame && hasJunction){ - //the gap is fake, it's just conveyors that can be replaced with junctions - i ++; - continue outer; - }else{ - //found a link, assign bridges - cur.block = bridge; - other.block = bridge; - i = j; - continue outer; - } - } - - if(other.tile() != null && !avoid.get(other.tile().block())){ - wereSame = false; - } - } - - //if it got here, that means nothing was found. this likely means there's a bunch of stuff at the end; add it and bail out - for(int j = i + 1; j < plans.size; j++){ - result.add(plans.get(j)); - } - break; - }else{ - i ++; - } - } - - plans.set(result); + calculateBridges(plans, new DirectionBridgePlacer(bridge), hasJunction, avoid); } private static float tileHeuristic(Tile tile, Tile other){ diff --git a/core/src/mindustry/logic/LDrawable.java b/core/src/mindustry/logic/LDrawable.java new file mode 100644 index 0000000000..6f27140206 --- /dev/null +++ b/core/src/mindustry/logic/LDrawable.java @@ -0,0 +1,8 @@ +package mindustry.logic; + +import arc.struct.*; + +public interface LDrawable { + boolean drawable(LExecutor exec); + void draw(LongSeq buffer); +} diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index aae498aa55..52531cdbe5 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1040,8 +1040,8 @@ public class LExecutor{ @Override public void run(LExecutor exec){ - if(target.building() instanceof LogicDisplayBuild d && d.isValid() && (d.team == exec.team || exec.privileged)){ - d.flushCommands(exec.graphicsBuffer); + if(target.building() instanceof LDrawable d && d.drawable(exec)){ + d.draw(exec.graphicsBuffer); } exec.graphicsBuffer.clear(); } diff --git a/core/src/mindustry/mod/DataImagePacker.java b/core/src/mindustry/mod/DataImagePacker.java index 0eee692103..514b592bb4 100644 --- a/core/src/mindustry/mod/DataImagePacker.java +++ b/core/src/mindustry/mod/DataImagePacker.java @@ -10,6 +10,7 @@ import arc.struct.*; import arc.util.*; import arc.util.Log.*; import mindustry.*; +import mindustry.game.EventType.*; import mindustry.mod.data.*; import java.io.*; @@ -21,8 +22,12 @@ public class DataImagePacker{ private static int defaultAtlasDepth = -1; public static final String regionPrefix = "dp-"; + /** Prefix for images pushed dynamically by the server via {@link #addTexture(String, byte[])}, as opposed to map/mod data patches. */ + public static final String serverRegionPrefix = "net-"; private @Nullable Seq addedRegions; + /** Textures added at runtime via addTexture(), keyed by their unprefixed name. Tracked separately from patchAtlas so they can be added/removed individually. */ + private ObjectMap serverImages = new ObjectMap<>(); /** Packs a new set of images. If images are already packed, disposes of the old ones. */ public void pack(Seq images){ @@ -159,6 +164,62 @@ public class DataImagePacker{ } addedRegions = null; } + + serverImages.each((name, texture) -> { + Core.atlas.getRegionMap().remove(serverRegionPrefix + name); + Core.atlas.getTextures().remove(texture); + texture.dispose(); + Core.atlas.getDrawables().remove(serverRegionPrefix + name); + + }); + serverImages.clear(); + } + + /** Decodes PNG bytes and registers them into the atlas under "netRegionPrefix + name", replacing any existing image with the same name. Safe to call from any thread. */ + public void addTexture(String name, byte[] pngData){ + Vars.mainExecutor.submit(() -> { + Pixmap pixmap; + try{ + pixmap = new Pixmap(pngData); + }catch(Throwable e){ + Log.err("Invalid server-provided image data for '" + name + "'", e); + return; + } + + if(pixmap.width > DataPatcher.maxImageSize || pixmap.height > DataPatcher.maxImageSize){ + Log.err("Server-provided image '" + name + "' exceeds max size of " + DataPatcher.maxImageSize); + pixmap.dispose(); + return; + } + + Core.app.post(() -> { + removeTexture(name); + + Texture texture = new Texture(pixmap); + pixmap.dispose(); + + var region = new TextureAtlas.AtlasRegion(texture, 0, 0, texture.width, texture.height); + region.name = serverRegionPrefix + name; + + serverImages.put(name, texture); + Core.atlas.getTextures().add(texture); + Core.atlas.getRegionMap().put(serverRegionPrefix + name, region); + + //fired with the full atlas region name + Events.fire(new TextureStreamEvent(serverRegionPrefix + name)); + }); + }); + } + + /** Removes a texture previously added with {@link #addTexture} */ + public void removeTexture(String name){ + Texture texture = serverImages.remove(name); + if(texture != null){ + Core.atlas.getRegionMap().remove(serverRegionPrefix + name); + Core.atlas.getTextures().remove(texture); + texture.dispose(); + Core.atlas.getDrawables().remove(serverRegionPrefix + name); + } } public void printStats(PixmapPacker packer){ diff --git a/core/src/mindustry/mod/DataManager.java b/core/src/mindustry/mod/DataManager.java index 2af877392b..069ae517b3 100644 --- a/core/src/mindustry/mod/DataManager.java +++ b/core/src/mindustry/mod/DataManager.java @@ -7,6 +7,7 @@ import arc.graphics.g2d.TextureAtlas.*; import arc.struct.*; import arc.util.*; import mindustry.*; +import mindustry.annotations.Annotations.*; import mindustry.ctype.*; import mindustry.graphics.*; import mindustry.mod.data.*; @@ -184,6 +185,18 @@ public class DataManager{ reloadImages(); } + /** Adds/replaces a single image pushed by the server at runtime, independent of map/mod data patches. + * Use {@link mindustry.core.NetServer#sendTexture} to send a texture to connected clients. */ + public void addTexture(String name, byte[] pngData){ + if(!Vars.headless) packer.addTexture(name, pngData); + } + + /** Removes a texture previously added with {@link #addTexture}. */ + @Remote(variants = Variant.both) + public static void removeTexture(String name){ + if(!Vars.headless) Vars.state.data.packer.removeTexture(name); + } + public void reloadAudio(){ soundLoader.unload(); soundLoader.load(getSounds(), getMusic()); diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java index 148ed591c0..6fb04fbcb4 100644 --- a/core/src/mindustry/mod/Scripts.java +++ b/core/src/mindustry/mod/Scripts.java @@ -93,7 +93,7 @@ public class Scripts implements Disposable{ private boolean run(String script, String file, boolean wrap){ try{ - if(currentMod != null){ + if(currentMod != null && wrap){ //inject script info into file context.evaluateString(scope, "modName = \"" + currentMod.name + "\"\nscriptName = \"" + file + "\"", "initscript.js", 1); } diff --git a/core/src/mindustry/net/Net.java b/core/src/mindustry/net/Net.java index 53aea8b5fd..894684c2a7 100644 --- a/core/src/mindustry/net/Net.java +++ b/core/src/mindustry/net/Net.java @@ -22,7 +22,7 @@ import static mindustry.Vars.*; @SuppressWarnings("unchecked") public class Net{ - public static final int packetIdAssetStream, packetIdWorldStream; + public static final int packetIdAssetStream, packetIdWorldStream, packetIdTextureStream; private static Seq> packetProvs = new Seq<>(); private static Seq> packetClasses = new Seq<>(); @@ -50,6 +50,7 @@ public class Net{ registerPacket(ConnectPacket::new); registerPacket(AssetRequirementStream::new); packetIdAssetStream = registerPacket(AssetStream::new); + packetIdTextureStream = registerPacket(TextureStream::new); //register generated packet classes Call.registerPackets(); diff --git a/core/src/mindustry/net/Packets.java b/core/src/mindustry/net/Packets.java index e6d8ad9638..403e356539 100644 --- a/core/src/mindustry/net/Packets.java +++ b/core/src/mindustry/net/Packets.java @@ -71,6 +71,10 @@ public class Packets{ } + public static class TextureStream extends Streamable{ + + } + public static class AssetRequirementStream extends Streamable{ } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index e5ca57d181..ebedf24285 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -806,7 +806,7 @@ public class UnitType extends UnlockableContent implements Senseable{ stats.add(Stat.targetsAir, targetAir); stats.add(Stat.targetsGround, targetGround); - if(abilities.any()){ + if(abilities.contains(a -> a.display)){ stats.add(Stat.abilities, StatValues.abilities(abilities)); } @@ -2000,6 +2000,7 @@ public class UnitType extends UnlockableContent implements Senseable{ public static class UnitEngine implements Cloneable{ public float x, y, radius, rotation; + public @Nullable Color color; public UnitEngine(float x, float y, float radius, float rotation){ this.x = x; @@ -2018,7 +2019,7 @@ public class UnitType extends UnlockableContent implements Senseable{ if(scale <= 0.0001f) return; float rot = unit.rotation - 90; - Color color = type.engineColor == null ? unit.team.color : type.engineColor; + Color color = this.color != null ? this.color : type.engineColor == null ? unit.team.color : type.engineColor; Tmp.v1.set(x, y).rotate(rot); float ex = Tmp.v1.x, ey = Tmp.v1.y; diff --git a/core/src/mindustry/ui/Elems.java b/core/src/mindustry/ui/Elems.java new file mode 100644 index 0000000000..3437d2983a --- /dev/null +++ b/core/src/mindustry/ui/Elems.java @@ -0,0 +1,40 @@ +package mindustry.ui; + +import arc.func.*; +import arc.scene.ui.*; +import arc.util.*; +import mindustry.gen.*; + +public class Elems{ + + /** Fancy check box with a background. Not a real check box. */ + public static Button check(String text, Boolp checked, Boolc listener){ + return check(text, checked != null && checked.get(), checked, listener); + } + + /** Fancy check box with a background. Not a real check box. */ + public static Button check(String text, boolean checked, Boolc listener){ + return check(text, checked, null, listener); + } + + /** Fancy check box with a background. Not a real check box. */ + public static Button check(String text, boolean currentChecked, @Nullable Boolp checked, Boolc listener){ + Button box = new Button(Styles.grayt); + box.background(Styles.grayPanel); + box.margin(10f); + + box.add(new Image()).update(i -> i.setDrawable(box.isOver() ? (box.isChecked() ? Tex.checkOnOver : Tex.checkOver) : box.isChecked() ? Tex.checkOn : Tex.checkOff)) + .size(32f).padRight(8f).padLeft(-4f); + + box.add(text); + box.setChecked(currentChecked); + + if(checked != null) box.update(() -> box.setChecked(checked.get())); + + box.clicked(() -> listener.get(box.isChecked())); + + box.left(); + + return box; + } +} diff --git a/core/src/mindustry/ui/Menus.java b/core/src/mindustry/ui/Menus.java index e7b7ee7fd2..4f4b914e1d 100644 --- a/core/src/mindustry/ui/Menus.java +++ b/core/src/mindustry/ui/Menus.java @@ -83,7 +83,9 @@ public class Menus{ } }; - dialog.idToElement = UiTreeBuilder.build(dialog.cont, builder, dialog.originalListener); + var result = UiTreeBuilder.build(dialog.cont, builder, dialog.originalListener); + dialog.idToElement = result.idElements; + dialog.addImages(result.images); var oldDialog = menuDialogs.remove(id); @@ -108,12 +110,13 @@ public class Menus{ if(table == null) return; table.clear(); - var newElements = UiTreeBuilder.build(table, builder, result -> { + var result = UiTreeBuilder.build(table, builder, res -> { //forward results to original listener - dialog.originalListener.get(result); + dialog.originalListener.get(res); }); - //save new element IDs - dialog.idToElement.putAll(newElements); + //save new element IDs and images + dialog.idToElement.putAll(result.idElements); + dialog.addImages(result.images); } @Remote(variants = Variant.both) @@ -388,12 +391,31 @@ public class Menus{ static class MenuDialog extends BaseDialog{ //retained for easy lookup if an element needs to be replaced ObjectMap idToElement; + //every streamed image built into this dialog, keyed by the region name + ObjectMap> images = new ObjectMap<>(); Cons originalListener; boolean wasHidden; + static{ + // refresh images that are received after the dialog is already opened + Events.on(TextureStreamEvent.class, e -> { + for(var dialog : menuDialogs.values()){ + var imgs = dialog.images.get(e.name); + if(imgs != null){ + var drawable = Core.atlas.drawable(e.name); + imgs.each(img -> img.setDrawable(drawable)); + } + } + }); + } + public MenuDialog(){ super(""); } + + void addImages(ObjectMap> images){ + images.each((region, imgs) -> this.images.get(region, Seq::new).addAll(imgs)); + } } public interface MenuListener{ diff --git a/core/src/mindustry/ui/builder/UiBuilder.java b/core/src/mindustry/ui/builder/UiBuilder.java index cfb49b5ebc..cd8aa670ca 100644 --- a/core/src/mindustry/ui/builder/UiBuilder.java +++ b/core/src/mindustry/ui/builder/UiBuilder.java @@ -253,6 +253,7 @@ public class UiBuilder{ ImageBuilder(){ super(UiKey.image); } public ImageBuilder region(String region){ return prop(UiKey.region, region); } + public ImageBuilder placeholder(String placeholder){ return prop(UiKey.placeholder, placeholder); } public ImageBuilder scaling(Scaling scaling){ return prop(UiKey.scaling, scaling.name()); } } @@ -261,6 +262,7 @@ public class UiBuilder{ public ButtonBuilder text(String text){ return prop(UiKey.text, text); } public ButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); } + public ButtonBuilder placeholder(String placeholder){ return prop(UiKey.placeholder, placeholder); } public ButtonBuilder style(String style){ return prop(UiKey.style, style); } public ButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); } public ButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } @@ -272,6 +274,7 @@ public class UiBuilder{ ImageButtonBuilder(){ super(UiKey.imageButton); } public ImageButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); } + public ImageButtonBuilder placeholder(String placeholder){ return prop(UiKey.placeholder, placeholder); } public ImageButtonBuilder style(String style){ return prop(UiKey.style, style); } public ImageButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); } public ImageButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); } diff --git a/core/src/mindustry/ui/builder/UiKey.java b/core/src/mindustry/ui/builder/UiKey.java index f5cae29005..bddafef9d1 100644 --- a/core/src/mindustry/ui/builder/UiKey.java +++ b/core/src/mindustry/ui/builder/UiKey.java @@ -6,7 +6,7 @@ public enum UiKey{ table, pane, stack, label, image, button, imageButton, field, check, slider, space, defaults, buttonTable, row, //NOTE: all node types must be before 'row' // node-specific properties - text, wrap, region, icon, scaling, background, margin, id, hint, maxLength, checked, min, max, step, + text, wrap, region, icon, placeholder, scaling, background, margin, id, hint, maxLength, checked, min, max, step, defaultValue, clicked, enter, style, group, condition, color, disabled, // cell properties diff --git a/core/src/mindustry/ui/builder/UiTreeBuilder.java b/core/src/mindustry/ui/builder/UiTreeBuilder.java index 29f3fdaeda..cac9bd8fc8 100644 --- a/core/src/mindustry/ui/builder/UiTreeBuilder.java +++ b/core/src/mindustry/ui/builder/UiTreeBuilder.java @@ -20,20 +20,21 @@ import arc.scene.utils.*; import arc.struct.*; import arc.util.*; import mindustry.gen.*; +import mindustry.mod.*; import mindustry.ui.*; import mindustry.ui.builder.UiBuilder.*; /** Builds a UI into an existing Table from a NodeBuilder tree. */ public class UiTreeBuilder{ - public static ObjectMap build(Table root, NodeBuilder builder){ + public static BuildResult build(Table root, NodeBuilder builder){ return build(root, builder, null); } - public static ObjectMap build(Table root, NodeBuilder builder, @Nullable Cons resultListener){ + public static BuildResult build(Table root, NodeBuilder builder, @Nullable Cons resultListener){ BuildContext ctx = new BuildContext(resultListener); buildInto(root, builder.entries, ctx); - return ctx.idElements; + return new BuildResult(ctx.idElements, ctx.images); } private static void buildInto(Table table, Seq entries, BuildContext ctx){ @@ -98,7 +99,7 @@ public class UiTreeBuilder{ var element = addNode(entry.key, child, ctx); if(element == null) continue; // unknown/unsupported node - skip for forward compat - String id = node.str(UiKey.id); + String id = child.str(UiKey.id); if(id != null){ element.name = id; @@ -125,7 +126,10 @@ public class UiTreeBuilder{ yield label; } case image -> { - Image image = new Image(findDrawable(node.str(UiKey.region, node.str(UiKey.icon, "error")))); + String region = node.str(UiKey.region, node.str(UiKey.icon, "error")); + Image image = new Image(findDrawable(region, node.str(UiKey.placeholder))); + // track server-streamed images so that they can be reloaded when streaming finishes + if(region.startsWith(DataImagePacker.serverRegionPrefix)) ctx.images.get(region, Seq::new).add(image); String scl = node.str(UiKey.scaling); if(scl != null){ try{ @@ -141,7 +145,10 @@ public class UiTreeBuilder{ String icon = node.str(UiKey.icon); if(icon != null){ - btn.add(new Image(findDrawable(icon)).setScaling(Scaling.fit)).size(32f); + Image iconImage = new Image(findDrawable(icon, node.str(UiKey.placeholder))).setScaling(Scaling.fit); + // track server-streamed images so that they can be reloaded when streaming finishes + if(icon.startsWith(DataImagePacker.serverRegionPrefix)) ctx.images.get(icon, Seq::new).add(iconImage); + btn.add(iconImage).size(32f); btn.getCells().reverse(); } wireButton(btn, node, ctx); @@ -149,7 +156,9 @@ public class UiTreeBuilder{ } case imageButton -> { String icon = node.str(UiKey.icon); - ImageButton btn = new ImageButton(icon != null ? findDrawable(icon) : null); + ImageButton btn = new ImageButton(icon != null ? findDrawable(icon, node.str(UiKey.placeholder)) : null); + // track server-streamed images so that they can be reloaded when streaming finishes + if(icon != null && icon.startsWith(DataImagePacker.serverRegionPrefix)) ctx.images.get(icon, Seq::new).add(btn.getImage()); applyStyle(node, ImageButtonStyle.class, btn::setStyle); wireButton(btn, node, ctx); yield btn; @@ -308,13 +317,20 @@ public class UiTreeBuilder{ } private static Drawable findDrawable(String name){ + return findDrawable(name, "error"); + } + + /** @param placeholder shown instead of the error sprite if {@code name} isn't streamed from the server yet */ + private static Drawable findDrawable(String name, @Nullable String placeholder){ if(Core.atlas.has(name)){ return Core.atlas.drawable(name); } //icons are a fallback (TODO: bad idea?) var result = Icon.icons.get(name); - if(result == null) return Core.atlas.drawable("error"); - return result; + if(result != null) return result; + + if(placeholder != null) return findDrawable(placeholder, null); + return Core.atlas.drawable("nomap"); // no placeholder provided, use the default of the map preview texture } /** Evaluates a condition string: "portrait", "landscape", or "width|height >=|>|<|<= number". */ @@ -363,6 +379,7 @@ public class UiTreeBuilder{ final @Nullable Cons resultListener; final ObjectMap idElements = new ObjectMap<>(); final ObjectMap> buttonGroups = new ObjectMap<>(); + final ObjectMap> images = new ObjectMap<>(); BuildContext(@Nullable Cons resultListener){ this.resultListener = resultListener; @@ -372,4 +389,15 @@ public class UiTreeBuilder{ return buttonGroups.get(name, ButtonGroup::new); } } + + /** Result of building a NodeBuilder: named elements (those with an explicit id), plus any server-streamed images, keyed by region name */ + public static class BuildResult{ + public final ObjectMap idElements; + public final ObjectMap> images; + + BuildResult(ObjectMap idElements, ObjectMap> images){ + this.idElements = idElements; + this.images = images; + } + } } \ No newline at end of file diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java index 8a73de0282..50e4f8d84f 100644 --- a/core/src/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/mindustry/ui/dialogs/JoinDialog.java @@ -779,11 +779,9 @@ public class JoinDialog extends BaseDialog{ }else if(host.version == 0){ return Core.bundle.get("server.outdated"); }else if(host.version < Version.build && Version.build != -1){ - return Core.bundle.get("server.outdated") + "\n" + - Core.bundle.format("server.version", host.version, host.versionType); + return "\n" + Core.bundle.format("server.version", host.version, host.versionType) + " " + Core.bundle.get("server.outdated"); }else if(host.version > Version.build && Version.build != -1){ - return Core.bundle.get("server.outdated.client") + "\n" + - Core.bundle.format("server.version", host.version, host.versionType); + return "\n" + Core.bundle.format("server.version", host.version, host.versionType) + " " + Core.bundle.get("server.outdated.client"); }else if(host.version == Version.build && Version.type.equals(host.versionType)){ //not important return ""; diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java index ed09a51d27..b4eadd11f9 100644 --- a/core/src/mindustry/ui/dialogs/ModsDialog.java +++ b/core/src/mindustry/ui/dialogs/ModsDialog.java @@ -206,14 +206,22 @@ public class ModsDialog extends BaseDialog{ t.table(title1 -> { title1.left(); - title1.add(new BorderImage(){{ + title1.add(new BorderImage()).size(h - 8f).padTop(-8f).padLeft(-8f).padRight(8f).with(b -> { if(mod.iconTexture != null){ - setDrawable(new TextureRegion(mod.iconTexture)); + b.setDrawable(new TextureRegion(mod.iconTexture)); }else{ - setDrawable(Tex.nomap); + b.setDrawable(Tex.nomap); + //if the texture gets loaded with a delay, update it + b.update(() -> { + if(mod.iconTexture != null){ + b.setDrawable(new TextureRegion(mod.iconTexture)); + //stop updating + b.update(null); + } + }); } - border(Pal.accent); - }}).size(h - 8f).padTop(-8f).padLeft(-8f).padRight(8f); + b.border(Pal.accent); + }); title1.table(text -> { boolean hideDisabled = !mod.isSupported() || mod.hasUnmetDependencies() || mod.hasContentErrors(); diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index b555d0544e..bb0442435f 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -58,12 +58,16 @@ public class PausedDialog extends BaseDialog{ }); if(!mobile){ + float tpad = -60f; + if(steam){ + tpad = 0f; + cont.check("@steam.friendsonly", !Core.settings.getBool("steampublichost2"), val -> { Core.settings.put("steampublichost2", !val); platform.updateLobby(); }).colspan(2).left().with(c -> ui.addDescTooltip(c, "@steam.friendsonly.tooltip")).width(440f) - .visible(() -> net.server()).center().colspan(2).fillX().padBottom(10f).row(); + .visible(() -> net.server()).center().colspan(2).fillX().padBottom(10f).padTop(-60f).row(); } float dw = 220f; @@ -72,10 +76,10 @@ public class PausedDialog extends BaseDialog{ boolean showObjective = state.rules.sector != null && state.rules.sector.preset != null && state.rules.sector.preset.description != null; if(showObjective){ - cont.button("@objective", Icon.info, () -> ui.fullText.show("@objective", state.rules.sector != null && state.rules.sector.preset != null ? state.rules.sector.preset.description : "oh dear")).padTop(-60f); + cont.button("@objective", Icon.info, () -> ui.fullText.show("@objective", state.rules.sector != null && state.rules.sector.preset != null ? state.rules.sector.preset.description : "oh dear")).padTop(tpad); } - cont.button("@abandon", Icon.cancel, () -> ui.planet.abandonSectorConfirm(state.rules.sector, this::hide)).padTop(-60f) + cont.button("@abandon", Icon.cancel, () -> ui.planet.abandonSectorConfirm(state.rules.sector, this::hide)).padTop(tpad) .colspan(showObjective ? 1 : 2).width(showObjective ? dw : dw * 2 + 10f) .disabled(b -> net.client() || state.gameOver).visible(() -> state.rules.sector != null).row(); diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 72ca268651..d5fe8b341d 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -439,7 +439,7 @@ public class SettingsMenuDialog extends BaseDialog{ graphics.sliderPref("screenshake", 4, 0, 8, i -> (i / 4f) + "x"); - graphics.sliderPref("bloomintensity", 6, 0, 16, i -> (int)(i/4f * 100f) + "%"); + graphics.sliderPref("bloomintensity", 6, 0, 16, i -> (int)(i / 4f * 100f) + "%"); graphics.sliderPref("bloomblur", 2, 1, 16, i -> i + "x"); graphics.sliderPref("fpscap", 240, 10, 245, 5, s -> { @@ -810,25 +810,13 @@ public class SettingsMenuDialog extends BaseDialog{ @Override public void add(SettingsTable table){ - Button box = new Button(Styles.grayt); - box.background(Styles.grayPanel); - box.margin(10f); - - box.add(new Image()).update(i -> i.setDrawable(box.isOver() ? (box.isChecked() ? Tex.checkOnOver : Tex.checkOver) : box.isChecked() ? Tex.checkOn : Tex.checkOff)) - .size(32f).padRight(8f).padLeft(-4f); - - box.add(title); - - box.update(() -> box.setChecked(settings.getBool(name))); - - box.clicked(() -> { - settings.put(name, box.isChecked()); + Table box = Elems.check(title, () -> settings.getBool(name), value -> { + settings.put(name, value); if(changed != null){ - changed.get(box.isChecked()); + changed.get(value); } }); - box.left(); addDesc(table.add(box).minWidth(Math.min(500f, Core.graphics.getWidth() / 1.2f / Scl.scl(1f))).fillX().height(45f).left().padTop(7f).get()); table.row(); } diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index d9f9733442..098dce52e1 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -785,6 +785,44 @@ public class Tile implements Position, QuadTreeObject, Displayable{ } } + /** Fills an area. x2/y2 are inclusive.*/ + @Remote(called = Loc.server) + public static void fillTileBlocks(int x, int y, int x2, int y2, Block block, Team team){ + for(int tx = x; tx <= x2; tx++){ + for(int ty = y; ty <= y2; ty++){ + Tile tile = world.tile(tx, ty); + if(tile == null) return; //out of bounds; return instead of breaking since it's a rectangle + tile.setBlock(block, team, 0); + } + } + } + + /** Fills an area. x2/y2 are inclusive.*/ + @Remote(called = Loc.server) + public static void fillTileFloors(int x, int y, int x2, int y2, Block block){ + if(!(block instanceof Floor floor)) return; + for(int tx = x; tx <= x2; tx++){ + for(int ty = y; ty <= y2; ty++){ + Tile tile = world.tile(tx, ty); + if(tile == null) return; //out of bounds; return instead of breaking since it's a rectangle + tile.setFloor(floor); + } + } + } + + /** Fills an area. x2/y2 are inclusive.*/ + @Remote(called = Loc.server) + public static void fillTileOverlays(int x, int y, int x2, int y2, Block block){ + if(!(block instanceof OverlayFloor floor)) return; + for(int tx = x; tx <= x2; tx++){ + for(int ty = y; ty <= y2; ty++){ + Tile tile = world.tile(tx, ty); + if(tile == null) return; //out of bounds; return instead of breaking since it's a rectangle + tile.setOverlay(floor); + } + } + } + @Remote(called = Loc.server) public static void setFloor(Tile tile, Block floor, Block overlay){ tile.setFloor(floor.asFloor()); diff --git a/core/src/mindustry/world/blocks/defense/ForceProjector.java b/core/src/mindustry/world/blocks/defense/ForceProjector.java index 049c91662b..6f7626808e 100644 --- a/core/src/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/mindustry/world/blocks/defense/ForceProjector.java @@ -242,7 +242,7 @@ public class ForceProjector extends Block{ if(buildup >= shieldHealth + phaseShieldBoost * phaseHeat && !broken){ broken = true; buildup = shieldHealth; - shieldBreakEffect.at(x, y, realRadius(), team.color); + shieldBreakEffect.at(x, y, realRadius(), team.color, sides); breakSound.at(x, y); if(team != state.rules.defaultTeam){ Events.fire(Trigger.forceProjectorBreak); diff --git a/core/src/mindustry/world/blocks/logic/LogicDisplay.java b/core/src/mindustry/world/blocks/logic/LogicDisplay.java index 0e63dc887a..96feb13b11 100644 --- a/core/src/mindustry/world/blocks/logic/LogicDisplay.java +++ b/core/src/mindustry/world/blocks/logic/LogicDisplay.java @@ -81,7 +81,7 @@ public class LogicDisplay extends Block{ clipSize = Math.max(clipSize, scaleFactor * Draw.scl * displaySize); } - public class LogicDisplayBuild extends Building{ + public class LogicDisplayBuild extends Building implements LDrawable{ //The root display (bottom left corner of display for tileable displays) public LogicDisplayBuild rootDisplay = this; public @Nullable FrameBuffer buffer; @@ -121,7 +121,13 @@ public class LogicDisplay extends Block{ }; } - public void flushCommands(LongSeq graphicsBuffer){ + @Override + public boolean drawable(LExecutor exec){ + return isValid() && (exec.privileged || (team == exec.team && !privileged)); + } + + @Override + public void draw(LongSeq graphicsBuffer){ int added = Math.min(graphicsBuffer.size, LExecutor.maxDisplayBuffer - commands.size); for(int i = 0; i < added; i++){ diff --git a/core/src/mindustry/world/blocks/logic/TileableLogicDisplay.java b/core/src/mindustry/world/blocks/logic/TileableLogicDisplay.java index bfba55940e..bf67334753 100644 --- a/core/src/mindustry/world/blocks/logic/TileableLogicDisplay.java +++ b/core/src/mindustry/world/blocks/logic/TileableLogicDisplay.java @@ -258,11 +258,11 @@ public class TileableLogicDisplay extends LogicDisplay{ } @Override - public void flushCommands(LongSeq graphicsBuffer){ + public void draw(LongSeq graphicsBuffer){ if(isRoot()){ - super.flushCommands(graphicsBuffer); + super.draw(graphicsBuffer); }else{ - rootDisplay.flushCommands(graphicsBuffer); + rootDisplay.draw(graphicsBuffer); } } diff --git a/ios/build.gradle b/ios/build.gradle index 1feb743da7..b1d98203d4 100644 --- a/ios/build.gradle +++ b/ios/build.gradle @@ -4,7 +4,7 @@ buildscript{ } dependencies{ - classpath "com.mobidevelop.robovm:robovm-gradle-plugin:2.3.24" + classpath "com.mobidevelop.robovm:robovm-gradle-plugin:2.3.26" } } diff --git a/tools/extra-schemas/Research.json b/tools/extra-schemas/Research.json index 2cfe419eda..3c2e7d9316 100644 --- a/tools/extra-schemas/Research.json +++ b/tools/extra-schemas/Research.json @@ -2,7 +2,7 @@ "doc": "Tech tree placement for this content. Also accepts a plain string, treated as 'parent'.", "parent": {"type": "mindustry.ctype.UnlockableContent", "doc": "Name of the parent tech tree node; required unless 'root' is true."}, "requirements": {"type": "mindustry.type.ItemStack[]", "doc": "Overrides the default research cost, which is this content's build cost."}, - "objectives": {"type": "mindustry.type.Objective[]", "doc": "Extra objectives to complete; items/liquids get a Produce objective automatically."}, + "objectives": {"type": "mindustry.game.Objectives.Objective[]", "doc": "Extra objectives to complete; items/liquids get a Produce objective automatically."}, "planet": {"type": "mindustry.type.Planet", "doc": "Name of the planet this node belongs to; inherited from the parent otherwise."}, "root": {"type": "boolean", "doc": "If true, this is a root node and 'parent' is not required.", "default": "false"}, "name": {"type": "java.lang.String", "doc": "Display name for a root node; ignored for non-root nodes."}, diff --git a/tools/extra-schemas/mindustry.entities.part.DrawPart$PartProgress.json b/tools/extra-schemas/mindustry.entities.part.DrawPart.PartProgress.json similarity index 100% rename from tools/extra-schemas/mindustry.entities.part.DrawPart$PartProgress.json rename to tools/extra-schemas/mindustry.entities.part.DrawPart.PartProgress.json diff --git a/tools/extra-schemas/mindustry.game.Objectives.Objective.json b/tools/extra-schemas/mindustry.game.Objectives.Objective.json new file mode 100644 index 0000000000..3e3c0040aa --- /dev/null +++ b/tools/extra-schemas/mindustry.game.Objectives.Objective.json @@ -0,0 +1,4 @@ +{ + "abstract": true, + "doc": "Completion objective." +} diff --git a/tools/extra-schemas/mindustry.graphics.g3d.GenericMesh.json b/tools/extra-schemas/mindustry.graphics.g3d.GenericMesh.json new file mode 100644 index 0000000000..c6cef9e005 --- /dev/null +++ b/tools/extra-schemas/mindustry.graphics.g3d.GenericMesh.json @@ -0,0 +1,4 @@ +{ + "abstract": true, + "doc": "Planet mesh." +} diff --git a/tools/extra-schemas/mindustry.graphics.g3d.MatMesh.json b/tools/extra-schemas/mindustry.graphics.g3d.MatMesh.json index 0f2db70ae8..a1efa37049 100644 --- a/tools/extra-schemas/mindustry.graphics.g3d.MatMesh.json +++ b/tools/extra-schemas/mindustry.graphics.g3d.MatMesh.json @@ -1,5 +1,5 @@ { - "superclass": "java.lang.Object", + "superclass": "mindustry.graphics.g3d.GenericMesh", "mesh": { "type": "mindustry.graphics.g3d.GenericMesh", "doc": "Wrapped mesh to render with the extra transform applied. Required - no default." diff --git a/tools/extra-schemas/mindustry.graphics.g3d.MultiMesh.json b/tools/extra-schemas/mindustry.graphics.g3d.MultiMesh.json index 28fa99c3db..23ed51f5aa 100644 --- a/tools/extra-schemas/mindustry.graphics.g3d.MultiMesh.json +++ b/tools/extra-schemas/mindustry.graphics.g3d.MultiMesh.json @@ -1,5 +1,5 @@ { - "superclass": "java.lang.Object", + "superclass": "mindustry.graphics.g3d.GenericMesh", "meshes": { "type": "mindustry.graphics.g3d.GenericMesh[]", "doc": "Meshes to render together, in order. Required - no default." diff --git a/tools/extra-schemas/mindustry.graphics.g3d.PlanetMesh.json b/tools/extra-schemas/mindustry.graphics.g3d.PlanetMesh.json new file mode 100644 index 0000000000..912950175f --- /dev/null +++ b/tools/extra-schemas/mindustry.graphics.g3d.PlanetMesh.json @@ -0,0 +1,5 @@ +{ + "doc": "Planet mesh.", + "abstract": true, + "superclass": "mindustry.graphics.g3d.GenericMesh" +} diff --git a/tools/src/mindustry/tools/SchemaGenerator.java b/tools/src/mindustry/tools/SchemaGenerator.java index 1d08f2cb93..57541ad9f4 100644 --- a/tools/src/mindustry/tools/SchemaGenerator.java +++ b/tools/src/mindustry/tools/SchemaGenerator.java @@ -65,7 +65,17 @@ public class SchemaGenerator{ return; } - if(type.getSuperclass() != null) val.put("superclass", type.getSuperclass().getCanonicalName()); + if(Modifier.isAbstract(type.getModifiers())){ + val.put("abstract", true); + } + + if(type.getSuperclass() != null){ + val.put("superclass", type.getSuperclass().getCanonicalName()); + } + + if(Objective.class.isAssignableFrom(type)){ + val.put("superclass", Objective.class.getCanonicalName()); + } if(typeDec.getJavadoc().isPresent()){ val.put("doc", typeDec.getJavadoc().get().toText()); @@ -74,7 +84,7 @@ public class SchemaGenerator{ for(var field : type.getFields()){ if(Modifier.isStatic(field.getModifiers()) || field.getDeclaringClass() != type) continue; Jval inner = Jval.newObject(); - inner.put("type", field.getGenericType().getTypeName()); + inner.put("type", field.getGenericType().getTypeName().replace('$', '.')); if(field.isAnnotationPresent(Nullable.class)) inner.put("nullable", true); @@ -150,6 +160,14 @@ public class SchemaGenerator{ } """.replace("%TYPES%", allClasses.select(UnitType.class::isAssignableFrom).toString(", ", t -> "\"" + t.getSimpleName() + "\""))); + writeSchema(BuildVisibility.class.getCanonicalName(), """ + { + "doc": "Types of block build visibility.", + "superclass": "Enum", + "values": [%VALUES%] + } + """.replace("%VALUES%", Seq.with(BuildVisibility.class.getFields()).toString(", ", t -> "\"" + t.getName() + "\""))); + writeSchema(Interp.class.getCanonicalName(), """ { "doc": "All types of interpolation.", @@ -158,6 +176,14 @@ public class SchemaGenerator{ } """.replace("%VALUES%", Seq.with(Interp.class.getFields()).toString(", ", t -> "\"" + t.getName() + "\""))); + writeSchema(AIController.class.getCanonicalName(), """ + { + "doc": "Type of unit AI", + "superclass": "Enum", + "values": [%VALUES%] + } + """.replace("%VALUES%", decls.keys().toSeq().select(AIController.class::isAssignableFrom).toString(", ", t -> "\"" + t.getSimpleName() + "\""))); + writeSchema("allTeams", """ { "doc": "All team names.", @@ -186,6 +212,7 @@ public class SchemaGenerator{ } """.replace("%VALUES%", Seq.with(Sounds.class.getFields()).select(f -> f.getType() == Sound.class).toString(", ", t -> "\"" + t.getName() + "\""))); + new Fi("../../tools/extra-schemas").copyFilesTo(outputDir); } @@ -194,6 +221,7 @@ public class SchemaGenerator{ injectCustomField(UnitType.class, "template", field("TemplateUnitType", "UnitType template class.", false)); injectCustomField(UnitType.class, "defaultController", field(AIController.class.getCanonicalName(), "Unconditional controller; always assigned, even if on the player team. This overwrites RTS AI.", false)); injectCustomField(UnitType.class, "aiController", field(AIController.class.getCanonicalName(), "Controller used when the unit is not on the player team.", false)); + injectCustomField(UnitType.class, "controller", field(AIController.class.getCanonicalName(), "Controller used when the unit is not on the player team. Equivalent to aiController.", false)); injectCustomField(Planet.class, "sectorSize", field("int", "Planet sector subdivisions.", false)); diff --git a/tools/src/mindustry/tools/ScriptMainGenerator.java b/tools/src/mindustry/tools/ScriptMainGenerator.java index 6f7b3b03d7..da9bf6b31e 100644 --- a/tools/src/mindustry/tools/ScriptMainGenerator.java +++ b/tools/src/mindustry/tools/ScriptMainGenerator.java @@ -33,6 +33,7 @@ public class ScriptMainGenerator{ Seq> classes = Seq.withArrays( getClasses("mindustry"), + getClasses("arc"), getClasses("arc.func"), getClasses("arc.struct"), getClasses("arc.scene"),