Merge branch 'master' of https://github.com/Anuken/Mindustry into rendering-optimizations
This commit is contained in:
@@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/layout_root"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="10dp" >
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/gdxDialogsEditTextInput"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="text"
|
|
||||||
android:maxLines="1"
|
|
||||||
>
|
|
||||||
|
|
||||||
<requestFocus />
|
|
||||||
|
|
||||||
</EditText>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -27,7 +27,6 @@ import io.anuke.mindustry.io.SaveIO;
|
|||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.ui.dialogs.FileChooser;
|
import io.anuke.mindustry.ui.dialogs.FileChooser;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
|
||||||
import io.anuke.ucore.scene.ui.layout.Unit;
|
import io.anuke.ucore.scene.ui.layout.Unit;
|
||||||
import io.anuke.ucore.util.Bundles;
|
import io.anuke.ucore.util.Bundles;
|
||||||
import io.anuke.ucore.util.Strings;
|
import io.anuke.ucore.util.Strings;
|
||||||
@@ -54,11 +53,6 @@ public class AndroidLauncher extends PatchedAndroidApplication{
|
|||||||
config.depth = 0;
|
config.depth = 0;
|
||||||
Platform.instance = new Platform(){
|
Platform.instance = new Platform(){
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDialog(TextField field, int length){
|
|
||||||
TextFieldDialogListener.add(field, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openDonations(){
|
public void openDonations(){
|
||||||
showDonations();
|
showDonations();
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package io.anuke.mindustry;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.text.InputFilter;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager.LayoutParams;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
|
|
||||||
public class AndroidTextFieldDialog{
|
|
||||||
private Activity activity;
|
|
||||||
private EditText userInput;
|
|
||||||
private AlertDialog.Builder builder;
|
|
||||||
private TextPromptListener listener;
|
|
||||||
private boolean isBuild;
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog(){
|
|
||||||
this.activity = (Activity) Gdx.app;
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog show(){
|
|
||||||
|
|
||||||
activity.runOnUiThread(() -> {
|
|
||||||
AlertDialog dialog = builder.create();
|
|
||||||
|
|
||||||
dialog.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
||||||
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AndroidTextFieldDialog load(){
|
|
||||||
|
|
||||||
activity.runOnUiThread(() -> {
|
|
||||||
|
|
||||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
|
|
||||||
LayoutInflater li = LayoutInflater.from(activity);
|
|
||||||
|
|
||||||
View promptsView = li.inflate(getResourceId("gdxdialogs_inputtext", "layout"), null);
|
|
||||||
|
|
||||||
alertDialogBuilder.setView(promptsView);
|
|
||||||
|
|
||||||
userInput = promptsView.findViewById(getResourceId("gdxDialogsEditTextInput", "id"));
|
|
||||||
|
|
||||||
alertDialogBuilder.setCancelable(false);
|
|
||||||
builder = alertDialogBuilder;
|
|
||||||
|
|
||||||
isBuild = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait till TextPrompt is built.
|
|
||||||
while(!isBuild){
|
|
||||||
try{
|
|
||||||
Thread.sleep(10);
|
|
||||||
}catch(InterruptedException ignored){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getResourceId(String pVariableName, String pVariableType){
|
|
||||||
try{
|
|
||||||
return activity.getResources().getIdentifier(pVariableName, pVariableType, activity.getPackageName());
|
|
||||||
}catch(Exception e){
|
|
||||||
Gdx.app.error("Android Dialogs", "Cannot find resouce with name: " + pVariableName
|
|
||||||
+ " Did you copy the layouts to /res/layouts and /res/layouts_v14 ?");
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setText(CharSequence value){
|
|
||||||
userInput.append(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setCancelButtonLabel(CharSequence label){
|
|
||||||
builder.setNegativeButton(label, (dialog, id) -> dialog.cancel());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setConfirmButtonLabel(CharSequence label){
|
|
||||||
builder.setPositiveButton(label, (dialog, id) -> {
|
|
||||||
if(listener != null && !userInput.getText().toString().isEmpty()){
|
|
||||||
listener.confirm(userInput.getText().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setTextPromptListener(TextPromptListener listener){
|
|
||||||
this.listener = listener;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setInputType(int type){
|
|
||||||
userInput.setInputType(type);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidTextFieldDialog setMaxLength(int length){
|
|
||||||
userInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(length)});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TextPromptListener{
|
|
||||||
void confirm(String text);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package io.anuke.mindustry;
|
|
||||||
|
|
||||||
|
|
||||||
import android.text.InputType;
|
|
||||||
import com.badlogic.gdx.Application.ApplicationType;
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import io.anuke.ucore.scene.event.ChangeListener;
|
|
||||||
import io.anuke.ucore.scene.event.ClickListener;
|
|
||||||
import io.anuke.ucore.scene.event.InputEvent;
|
|
||||||
import io.anuke.ucore.scene.event.InputListener;
|
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
|
||||||
|
|
||||||
public class TextFieldDialogListener extends ClickListener{
|
|
||||||
private TextField field;
|
|
||||||
private int type;
|
|
||||||
private int max;
|
|
||||||
|
|
||||||
//type - 0 is text, 1 is numbers, 2 is decimals
|
|
||||||
public TextFieldDialogListener(TextField field, int type, int max){
|
|
||||||
this.field = field;
|
|
||||||
this.type = type;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void add(TextField field, int type, int max){
|
|
||||||
field.addListener(new TextFieldDialogListener(field, type, max));
|
|
||||||
field.addListener(new InputListener(){
|
|
||||||
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
|
|
||||||
Gdx.input.setOnscreenKeyboardVisible(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void add(TextField field){
|
|
||||||
add(field, 0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clicked(final InputEvent event, float x, float y){
|
|
||||||
|
|
||||||
if(Gdx.app.getType() == ApplicationType.Desktop) return;
|
|
||||||
|
|
||||||
AndroidTextFieldDialog dialog = new AndroidTextFieldDialog();
|
|
||||||
|
|
||||||
dialog.setTextPromptListener(text ->
|
|
||||||
Gdx.app.postRunnable(() -> {
|
|
||||||
field.clearText();
|
|
||||||
field.appendText(text);
|
|
||||||
field.fire(new ChangeListener.ChangeEvent());
|
|
||||||
Gdx.graphics.requestRendering();
|
|
||||||
}));
|
|
||||||
|
|
||||||
if(type == 0){
|
|
||||||
dialog.setInputType(InputType.TYPE_CLASS_TEXT);
|
|
||||||
}else if(type == 1){
|
|
||||||
dialog.setInputType(InputType.TYPE_CLASS_NUMBER);
|
|
||||||
}else if(type == 2){
|
|
||||||
dialog.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.setConfirmButtonLabel("OK").setText(field.getText());
|
|
||||||
dialog.setCancelButtonLabel("Cancel");
|
|
||||||
dialog.setMaxLength(max);
|
|
||||||
dialog.show();
|
|
||||||
event.cancel();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -23,9 +23,9 @@ allprojects {
|
|||||||
versionModifier = 'alpha'
|
versionModifier = 'alpha'
|
||||||
versionType = 'official'
|
versionType = 'official'
|
||||||
appName = 'Mindustry'
|
appName = 'Mindustry'
|
||||||
gdxVersion = '1.9.8'
|
gdxVersion = '1.9.9'
|
||||||
roboVMVersion = '2.3.0'
|
roboVMVersion = '2.3.0'
|
||||||
uCoreVersion = '2385d794f2cd3db2fb25d320f2223fa2148c25a0'
|
uCoreVersion = 'c93c55179ec05b44926d59c5878534a3177d804f'
|
||||||
|
|
||||||
getVersionString = {
|
getVersionString = {
|
||||||
String buildVersion = getBuildVersion()
|
String buildVersion = getBuildVersion()
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ block.metalfloor.name = Sol en métal
|
|||||||
block.deepwater.name = Eau profonde
|
block.deepwater.name = Eau profonde
|
||||||
block.water.name = Eau
|
block.water.name = Eau
|
||||||
block.lava.name = Lave
|
block.lava.name = Lave
|
||||||
block.tar.name = Tar
|
block.tar.name = Pétrole
|
||||||
block.blackstone.name = Roche sombre
|
block.blackstone.name = Roche sombre
|
||||||
block.stone.name = Roche
|
block.stone.name = Roche
|
||||||
block.dirt.name = Terre
|
block.dirt.name = Terre
|
||||||
@@ -623,7 +623,7 @@ block.rtg-generator.name = G.T.R.
|
|||||||
block.spectre.name = Spectre
|
block.spectre.name = Spectre
|
||||||
block.meltdown.name = Meltdown
|
block.meltdown.name = Meltdown
|
||||||
block.container.name = Conteneur
|
block.container.name = Conteneur
|
||||||
block.core.description = Sert de point de base pour le/les joueur(s). Ce bâtiment est le plus [SCARLET]important[] du jeu !
|
block.core.description = Sert de point de base pour le/les joueur(s). Ce bâtiment est le plus [scarlet]important[] du jeu !
|
||||||
team.blue.name = Bleu
|
team.blue.name = Bleu
|
||||||
team.red.name = Rouge
|
team.red.name = Rouge
|
||||||
team.orange.name = Orange
|
team.orange.name = Orange
|
||||||
@@ -709,73 +709,73 @@ block.plastanium-compressor.description = Produit du plastanium à partir de pé
|
|||||||
block.phase-weaver.description = Produit un tissu de phase à partir de thorium radioactif et de grandes quantités de sable.
|
block.phase-weaver.description = Produit un tissu de phase à partir de thorium radioactif et de grandes quantités de sable.
|
||||||
block.alloy-smelter.description = Produit un alliage de surtension à partir de titane, plomb, silicium et cuivre.
|
block.alloy-smelter.description = Produit un alliage de surtension à partir de titane, plomb, silicium et cuivre.
|
||||||
block.pulverizer.description = Brise la pierre en sable. Utile en cas de manque de sable naturel.
|
block.pulverizer.description = Brise la pierre en sable. Utile en cas de manque de sable naturel.
|
||||||
block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite.
|
block.pyratite-mixer.description = Mélange le charbon, le plomb et le sable en pyratite hautement inflammable.
|
||||||
block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound.
|
block.blast-mixer.description = Utilise du pétrole pour transformer la pyratite en un composé explosif moins inflammable mais plus explosif.
|
||||||
block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling.
|
block.cryofluidmixer.description = L'eau et le titane combinés forment un fluide cryo beaucoup plus efficace pour le refroidissement.
|
||||||
block.solidifer.description = Cools lava to stone at a fast pace.
|
block.solidifer.description = Refroidit la lave en pierre à un rythme rapide.
|
||||||
block.melter.description = Heats up stone to very high temperatures to obtain lava.
|
block.melter.description = Chauffe la pierre à des températures très élevées pour obtenir de la lave.
|
||||||
block.incinerator.description = Gets rid of any excess item or liquid.
|
block.incinerator.description = Se débarrasse de tout article ou liquide en excès.
|
||||||
block.biomattercompressor.description = Compresses biomatter in order to retrieve oil.
|
block.biomattercompressor.description = Compresse biomatter afin de récupérer du pétrole.
|
||||||
block.separator.description = Exposes stone to water pressure in order to obtain various minerals contained in the stone.
|
block.separator.description = Exposer la pierre à la pression de l'eau afin d'obtenir différents minéraux contenus dans la pierre.
|
||||||
block.centrifuge.description = More efficient than the separator, but also more expensive to build and requires power.
|
block.centrifuge.description = Plus efficace que le séparateur, mais aussi plus coûteux à construire et à alimenter.
|
||||||
block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks.
|
block.power-node.description = Transmet la puissance à des noeuds connectés. Il est possible de connecter jusqu'à quatre sources d'alimentation, puits ou nœuds.\nLe nœud recevra de l’alimentation ou fournira l’alimentation à tous les blocs adjacents.
|
||||||
block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes.
|
block.power-node-large.description = Son rayon d'action est supérieur à celui du nœud d'alimentation et peut être connecté à six sources d'alimentation, puits ou nœuds au maximum.
|
||||||
block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left.
|
block.battery.description = Stocke l’énergie chaque fois qu’il ya abondance et en cas de pénurie, tant qu’il reste de la capacité.
|
||||||
block.battery-large.description = Stores much more power than a regular battery.
|
block.battery-large.description = Stocke beaucoup plus d'énergie qu'une batterie ordinaire.
|
||||||
block.combustion-generator.description = Generates power by burning oil or flammable materials.
|
block.combustion-generator.description = Génère de l'énergie en brûlant du pétrole ou des matériaux inflammables.
|
||||||
block.turbine-generator.description = More efficient than a combustion generator, but requires additional water.
|
block.turbine-generator.description = Plus efficace qu'un générateur de combustion, mais nécessite de l'eau supplémentaire.
|
||||||
block.thermal-generator.description = Generates a large amount of power from lava.
|
block.thermal-generator.description = Génère une grande quantité d'énergie grâce à la lave.
|
||||||
block.solar-panel.description = Provides a small amount of power from the sun.
|
block.solar-panel.description = Fournit une petite quantité d'énergie grâce au soleil.
|
||||||
block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build.
|
block.solar-panel-large.description = Fournit une bien meilleure alimentation qu'un panneau solaire standard, mais coûte également beaucoup plus cher à construire.
|
||||||
block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied.
|
block.thorium-reactor.description = Génère d'énormes quantités d'énergie à partir de thorium hautement radioactif. Nécessite un refroidissement constant.\nExplose violemment si des quantités insuffisantes de liquide de refroidissement ne sont pas fournies.
|
||||||
block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor.
|
block.rtg-generator.description = Générateur thermoélectrique à radio-isotopes ne nécessitant pas de refroidissement mais fournissant moins d'énergie qu'un réacteur à thorium.
|
||||||
block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader.
|
block.unloader.description = Décharge des articles d'un conteneur, d'une chambre forte ou d'un noyau sur un convoyeur ou directement dans un bloc adjacent.\nLe type d'élément à décharger peut être modifié en tapotant sur le déchargeur.
|
||||||
block.container.description = Stores a small amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container.
|
block.container.description = Stocke une petite quantité d'objets. Utilisez-le pour créer des tampons lorsqu'il existe une demande non constante de matériaux. [LIGHT_GRAY]Un déchargeur[] peut être utilisé pour récupérer des éléments du conteneur.
|
||||||
block.vault.description = Stores a large amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault.
|
block.vault.description = Stocke une grande quantité d'objets. Utilisez-le pour créer des tampons lorsqu'il existe une demande non constante de matériaux. [LIGHT_GRAY]Un déchargeur[] peut être utilisé pour récupérer des éléments du coffre-fort.
|
||||||
block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely.
|
block.mechanical-drill.description = Un extracteur bon marché. Lorsqu'il est placé sur des carreaux appropriés, les objets sortent à un rythme lent et indéfiniment.
|
||||||
block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure.
|
block.pneumatic-drill.description = Un extracteur améliorée, plus rapide et capable de traiter des matériaux plus durs en utilisant la pression atmosphérique.
|
||||||
block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill.
|
block.laser-drill.description = Permet de forer encore plus rapidement grâce à la technologie laser, mais nécessite de l'énergie. De plus, le thorium radioactif peut être récupéré avec cet extracteur.
|
||||||
block.blast-drill.description = The ultimate drill. Requires large amounts of power.
|
block.blast-drill.description = L'extracteur ultime. Nécessite de grandes quantités d'énergie.
|
||||||
block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby.
|
block.water-extractor.description = Extrait l'eau du sol. Utilisez-le quand il n'y a pas de lac à proximité.
|
||||||
block.cultivator.description = Cultivates the soil with water in order to obtain biomatter.
|
block.cultivator.description = Cultiver le sol avec de l'eau afin d'obtenir du biomatter.
|
||||||
block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby.
|
block.oil-extractor.description = Utilise de grandes quantités d'énergie pour extraire le pétrole du sable. Utilisez-le lorsqu'il n'y a pas de source directe de pétrole à proximité.
|
||||||
block.dart-ship-pad.description = Leave your current vessel and change into a basic fighter aircraft.\nUse the pad by double tapping while standing on it.
|
block.dart-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en avion de combat de base.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it.
|
block.trident-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un bombardier lourd raisonnablement bien blindé.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it.
|
block.javelin-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un intercepteur puissant et rapide doté d’armes légères.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it.
|
block.glaive-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un grand vaisseau de combat bien blindé.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it.
|
block.tau-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un centre de support capable de soigner les bâtiments et unités amis.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it.
|
block.delta-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un méchant rapide, légèrement blindé, conçu pour les attaques à la volée.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it.
|
block.omega-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un mech encombrant et bien blindé, conçu pour les assauts de première ligne.\nUtilisez la plate-forme en tapotant deux fois dessus.
|
||||||
block.spirit-factory.description = Produces light drones which mine ore and repair blocks.
|
block.spirit-factory.description = Produit des drones légers qui extraient du minerai et réparent des blocs.
|
||||||
block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone.
|
block.phantom-factory.description = Produit des unités de drones avancées qui sont nettement plus efficaces qu'un drone spirituel.
|
||||||
block.wraith-factory.description = Produces fast, hit-and-run interceptor units.
|
block.wraith-factory.description = Produit des intercepteurs rapides qui harcèlent l'ennemi.
|
||||||
block.ghoul-factory.description = Produces heavy carpet bombers.
|
block.ghoul-factory.description = Produit des tapis de bombardiers lourds.
|
||||||
block.dagger-factory.description = Produces basic ground units.
|
block.dagger-factory.description = Produit des unités terrestres de base.
|
||||||
block.titan-factory.description = Produces advanced, armored ground units.
|
block.titan-factory.description = Produit des unités terrestres avancées et blindées.
|
||||||
block.fortress-factory.description = Produces heavy artillery ground units.
|
block.fortress-factory.description = Produit des unités terrestres d'artillerie lourde.
|
||||||
block.revenant-factory.description = Produces heavy laser ground units.
|
block.revenant-factory.description = Produit des unités terrestres laser lourdes.
|
||||||
block.repair-point.description = Continuously heals the closest damaged unit in its vicinity.
|
block.repair-point.description = Soigne en permanence l'unité endommagée la plus proche à proximité.
|
||||||
block.command-center.description = Allows changing friendly AI behavior. Currently, attack, retreat and patrol commands are supported.
|
block.command-center.description = Permet de changer le comportement amical de l'IA. Actuellement, les commandes d'attaque, de retraite et de patrouille sont prises en charge.
|
||||||
block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits.
|
block.conduit.description = Bloc de transport liquide de base. Fonctionne comme un convoyeur, mais avec des liquides. Utilisation optimale avec des extracteurs, des pompes ou d’autres conduits.
|
||||||
block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits.
|
block.pulse-conduit.description = Bloc de transport de liquide avancé. Transporte les liquides plus rapidement et stocke plus que des conduits standard.
|
||||||
block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles.
|
block.phase-conduit.description = Bloc de transport de liquide avancé. Utilise le pouvoir de téléporter des liquides vers un conduit de phase connecté sur plusieurs carreaux.
|
||||||
block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets.
|
block.liquid-router.description = Accepte les liquides d'une direction et les envoie dans 3 autres directions de manière égale. Peut également stocker une certaine quantité de liquide. Utile pour séparer les liquides d'une source à plusieurs cibles.
|
||||||
block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks.
|
block.liquid-tank.description = Stocke une grande quantité de liquides. Utilisez-le pour créer des tampons en cas de demande non constante de matériaux ou comme protection pour le refroidissement des blocs vitaux.
|
||||||
block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations.
|
block.liquid-junction.description = Agit comme un pont pour deux conduits de croisement. Utile dans les situations avec deux conduits différents transportant des liquides différents à des endroits différents.
|
||||||
block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building.
|
block.bridge-conduit.description = Bloc de transport de liquide avancé. Permet de transporter des liquides jusqu'à 3 tuiles de n'importe quel terrain ou bâtiment.
|
||||||
block.mechanical-pump.description = A cheap pump with slow output, but no power consumption.
|
block.mechanical-pump.description = Une pompe bon marché avec un débit lent, mais aucune consommation d'énergie.
|
||||||
block.rotary-pump.description = An advanced pump which doubles up speed by using power.
|
block.rotary-pump.description = Une pompe avancée qui double la vitesse en utilisant l’énergie.
|
||||||
block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava.
|
block.thermal-pump.description = La pompe ultime. Trois fois plus rapide qu'une pompe mécanique et la seule pompe capable de récupérer de la lave.
|
||||||
block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets.
|
block.router.description = Accepte les éléments d'une direction et les envoie dans 3 autres directions de manière égale. Utile pour séparer les matériaux d'une source en plusieurs cibles.
|
||||||
block.distributor.description = An advanced router which splits items to up to 7 other directions equally.
|
block.distributor.description = Un routeur avancé qui divise les articles en 7 autres directions de manière égale. [scarlet]Seule et unique ![]
|
||||||
block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building.
|
block.bridge-conveyor.description = Bloc de transport d'articles avancé. Permet de transporter des objets sur plus de 3 tuiles de n'importe quel terrain ou bâtiment.
|
||||||
block.alpha-mech-pad.description = When given enough power, rebuilds your ship into the[accent] Alpha[] mech.
|
block.alpha-mech-pad.description = Lorsque vous avez assez de puissance, reconstruisez votre vaisseau dans le [accent]Alpha[] mech.
|
||||||
block.itemsource.description = Infinitely outputs items. Sandbox only.
|
block.itemsource.description = Sort infiniment les articles. Bac à sable seulement.
|
||||||
block.liquidsource.description = Infinitely outputs liquids. Sandbox only.
|
block.liquidsource.description = Débit infini de liquides. Bac à sable seulement.
|
||||||
block.itemvoid.description = Destroys any items which go into it without using power. Sandbox only.
|
block.itemvoid.description = Détruit tous les objets qui y entrent sans utiliser d'énergie. Bac à sable seulement.
|
||||||
block.powerinfinite.description = Infinitely outputs power. Sandbox only.
|
block.powerinfinite.description = Débit infini d'énergie. Bac à sable seulement.
|
||||||
block.powervoid.description = Voids all power inputted into it. Sandbox only.
|
block.powervoid.description = Annule toute l'énergie qui y est introduite. Bac à sable seulement.
|
||||||
liquid.water.description = Commonly used for cooling machines and waste processing.
|
liquid.water.description = Couramment utilisé pour les machines de refroidissement et le traitement des déchets.
|
||||||
liquid.lava.description = Can be transformed into[LIGHT_GRAY] stone[], used for generating power or used as ammo for certain turrets.
|
liquid.lava.description = Peut être transformé en [LIGHT_GRAY]pierre[], utilisé pour générer de l'énergie ou utilisé comme munition pour certaines tourelles.
|
||||||
liquid.oil.description = Can be burnt, exploded or used as a coolant.
|
liquid.oil.description = Peut être brûlé, explosé ou utilisé comme liquide de refroidissement.
|
||||||
liquid.cryofluid.description = The most efficient liquid for cooling things down.
|
liquid.cryofluid.description = Le liquide de refroidissement le plus efficace.
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ import io.anuke.mindustry.game.Version;
|
|||||||
import io.anuke.mindustry.gen.Serialization;
|
import io.anuke.mindustry.gen.Serialization;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.world.blocks.defense.ForceProjector.ShieldEntity;
|
import io.anuke.mindustry.world.blocks.defense.ForceProjector.ShieldEntity;
|
||||||
|
import io.anuke.ucore.core.Settings;
|
||||||
import io.anuke.ucore.entities.Entities;
|
import io.anuke.ucore.entities.Entities;
|
||||||
import io.anuke.ucore.entities.EntityGroup;
|
import io.anuke.ucore.entities.EntityGroup;
|
||||||
import io.anuke.ucore.entities.impl.EffectEntity;
|
import io.anuke.ucore.entities.impl.EffectEntity;
|
||||||
import io.anuke.ucore.entities.trait.DrawTrait;
|
import io.anuke.ucore.entities.trait.DrawTrait;
|
||||||
import io.anuke.ucore.scene.ui.layout.Unit;
|
import io.anuke.ucore.scene.ui.layout.Unit;
|
||||||
import io.anuke.ucore.util.OS;
|
|
||||||
import io.anuke.ucore.util.Translator;
|
import io.anuke.ucore.util.Translator;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -169,7 +169,7 @@ public class Vars{
|
|||||||
ios = Gdx.app.getType() == ApplicationType.iOS;
|
ios = Gdx.app.getType() == ApplicationType.iOS;
|
||||||
android = Gdx.app.getType() == ApplicationType.Android;
|
android = Gdx.app.getType() == ApplicationType.Android;
|
||||||
|
|
||||||
dataDirectory = OS.getAppDataDirectory(appName);
|
dataDirectory = Settings.getDataDirectory(appName);
|
||||||
customMapDirectory = dataDirectory.child("maps/");
|
customMapDirectory = dataDirectory.child("maps/");
|
||||||
saveDirectory = dataDirectory.child("saves/");
|
saveDirectory = dataDirectory.child("saves/");
|
||||||
baseCameraScale = Math.round(Unit.dp.scl(4));
|
baseCameraScale = Math.round(Unit.dp.scl(4));
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ public class Blocks extends BlockList implements ContentList{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Registers build blocks from size 1-6
|
||||||
|
//no reference is needed here since they can be looked up by name later
|
||||||
for(int i = 1; i <= 6; i++){
|
for(int i = 1; i <= 6; i++){
|
||||||
new BuildBlock("build" + i);
|
new BuildBlock("build" + i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import io.anuke.mindustry.game.Teams;
|
|||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.ucore.core.Events;
|
import io.anuke.ucore.core.Events;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.unitGroups;
|
||||||
|
import static io.anuke.mindustry.Vars.waveTeam;
|
||||||
|
|
||||||
public class GameState{
|
public class GameState{
|
||||||
public int wave = 1;
|
public int wave = 1;
|
||||||
public float wavetime;
|
public float wavetime;
|
||||||
@@ -16,8 +19,13 @@ public class GameState{
|
|||||||
public Difficulty difficulty = Difficulty.normal;
|
public Difficulty difficulty = Difficulty.normal;
|
||||||
public WaveSpawner spawner = new WaveSpawner();
|
public WaveSpawner spawner = new WaveSpawner();
|
||||||
public Teams teams = new Teams();
|
public Teams teams = new Teams();
|
||||||
|
public int enemies;
|
||||||
private State state = State.menu;
|
private State state = State.menu;
|
||||||
|
|
||||||
|
public int enemies(){
|
||||||
|
return Net.client() ? enemies : unitGroups[waveTeam.ordinal()].size();
|
||||||
|
}
|
||||||
|
|
||||||
public void set(State astate){
|
public void set(State astate){
|
||||||
Events.fire(new StateChangeEvent(state, astate));
|
Events.fire(new StateChangeEvent(state, astate));
|
||||||
state = astate;
|
state = astate;
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public class Logic extends Module{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSectors(){
|
private void updateSectors(){
|
||||||
if(world.getSector() == null) return;
|
if(world.getSector() == null || state.gameOver) return;
|
||||||
|
|
||||||
world.getSector().currentMission().update();
|
world.getSector().currentMission().update();
|
||||||
|
|
||||||
@@ -218,12 +218,6 @@ public class Logic extends Module{
|
|||||||
if(group.isEmpty()) continue;
|
if(group.isEmpty()) continue;
|
||||||
|
|
||||||
EntityQuery.collideGroups(bulletGroup, group);
|
EntityQuery.collideGroups(bulletGroup, group);
|
||||||
EntityQuery.collideGroups(group, playerGroup);
|
|
||||||
|
|
||||||
for(EntityGroup other : unitGroups){
|
|
||||||
if(other.isEmpty()) continue;
|
|
||||||
EntityQuery.collideGroups(group, other);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityQuery.collideGroups(bulletGroup, playerGroup);
|
EntityQuery.collideGroups(bulletGroup, playerGroup);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import static io.anuke.mindustry.Vars.*;
|
|||||||
public class NetClient extends Module{
|
public class NetClient extends Module{
|
||||||
private final static float dataTimeout = 60 * 18;
|
private final static float dataTimeout = 60 * 18;
|
||||||
private final static float playerSyncTime = 2;
|
private final static float playerSyncTime = 2;
|
||||||
private final static float viewScale = 1.75f;
|
public final static float viewScale = 2f;
|
||||||
|
|
||||||
private Timer timer = new Timer(5);
|
private Timer timer = new Timer(5);
|
||||||
/**Whether the client is currently connecting.*/
|
/**Whether the client is currently connecting.*/
|
||||||
@@ -284,6 +284,7 @@ public class NetClient extends Module{
|
|||||||
//read wave info
|
//read wave info
|
||||||
state.wavetime = input.readFloat();
|
state.wavetime = input.readFloat();
|
||||||
state.wave = input.readInt();
|
state.wave = input.readInt();
|
||||||
|
state.enemies = input.readInt();
|
||||||
|
|
||||||
byte cores = input.readByte();
|
byte cores = input.readByte();
|
||||||
for(int i = 0; i < cores; i++){
|
for(int i = 0; i < cores; i++){
|
||||||
|
|||||||
@@ -412,7 +412,16 @@ public class NetServer extends Module{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWaitingForPlayers(){
|
public boolean isWaitingForPlayers(){
|
||||||
return state.mode.isPvp && playerGroup.size() < 2;
|
if(state.mode.isPvp){
|
||||||
|
int used = 0;
|
||||||
|
for(Team t : Team.all){
|
||||||
|
if(playerGroup.count(p -> p.getTeam() == t) > 0){
|
||||||
|
used ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return used < 2;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(){
|
public void update(){
|
||||||
@@ -468,6 +477,7 @@ public class NetServer extends Module{
|
|||||||
//write wave datas
|
//write wave datas
|
||||||
dataStream.writeFloat(state.wavetime);
|
dataStream.writeFloat(state.wavetime);
|
||||||
dataStream.writeInt(state.wave);
|
dataStream.writeInt(state.wave);
|
||||||
|
dataStream.writeInt(state.enemies());
|
||||||
|
|
||||||
ObjectSet<Tile> cores = state.teams.get(player.getTeam()).cores;
|
ObjectSet<Tile> cores = state.teams.get(player.getTeam()).cores;
|
||||||
|
|
||||||
@@ -494,7 +504,7 @@ public class NetServer extends Module{
|
|||||||
//check for syncable groups
|
//check for syncable groups
|
||||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||||
if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
|
if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
|
||||||
//clipping is done by represntatives
|
//clipping is done by representatives
|
||||||
SyncTrait represent = (SyncTrait) group.all().get(0);
|
SyncTrait represent = (SyncTrait) group.all().get(0);
|
||||||
|
|
||||||
//make sure mapping is enabled for this group
|
//make sure mapping is enabled for this group
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
package io.anuke.mindustry.core;
|
package io.anuke.mindustry.core;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.Input.Keys;
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.utils.Base64Coder;
|
import com.badlogic.gdx.utils.Base64Coder;
|
||||||
|
import io.anuke.ucore.core.Core;
|
||||||
import io.anuke.ucore.core.Settings;
|
import io.anuke.ucore.core.Settings;
|
||||||
|
import io.anuke.ucore.core.Timers;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
|
import io.anuke.ucore.scene.ui.Dialog;
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
import io.anuke.ucore.scene.ui.TextField;
|
||||||
|
import io.anuke.ucore.util.Log;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.mobile;
|
||||||
|
|
||||||
public abstract class Platform {
|
public abstract class Platform {
|
||||||
/**Each separate game platform should set this instance to their own implementation.*/
|
/**Each separate game platform should set this instance to their own implementation.*/
|
||||||
public static Platform instance = new Platform() {};
|
public static Platform instance = new Platform() {};
|
||||||
@@ -17,7 +25,34 @@ public abstract class Platform {
|
|||||||
addDialog(field, 16);
|
addDialog(field, 16);
|
||||||
}
|
}
|
||||||
/**See addDialog().*/
|
/**See addDialog().*/
|
||||||
public void addDialog(TextField field, int maxLength){}
|
public void addDialog(TextField field, int maxLength){
|
||||||
|
if(!mobile) return; //this is mobile only, desktop doesn't need dialogs
|
||||||
|
|
||||||
|
field.tapped(() -> {
|
||||||
|
Log.info("yappd");
|
||||||
|
Dialog dialog = new Dialog("", "dialog");
|
||||||
|
dialog.setFillParent(true);
|
||||||
|
dialog.content().top();
|
||||||
|
dialog.content().defaults().height(65f);
|
||||||
|
TextField to = dialog.content().addField(field.getText(), t-> {}).pad(15).width(250f).get();
|
||||||
|
to.setMaxLength(maxLength);
|
||||||
|
to.keyDown(Keys.ENTER, () -> dialog.content().find("okb").fireClick());
|
||||||
|
dialog.content().addButton("$text.ok", () -> {
|
||||||
|
field.clearText();
|
||||||
|
field.appendText(to.getText());
|
||||||
|
field.change();
|
||||||
|
dialog.hide();
|
||||||
|
Gdx.input.setOnscreenKeyboardVisible(false);
|
||||||
|
}).width(90f).name("okb");
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
Timers.runTask(1f, () -> {
|
||||||
|
to.setCursorPosition(to.getText().length());
|
||||||
|
Core.scene.setKeyboardFocus(to);
|
||||||
|
Gdx.input.setOnscreenKeyboardVisible(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
/**Update discord RPC.*/
|
/**Update discord RPC.*/
|
||||||
public void updateRPC(){}
|
public void updateRPC(){}
|
||||||
/**Called when the game is exited.*/
|
/**Called when the game is exited.*/
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class ThreadHandler{
|
|||||||
public ThreadHandler(){
|
public ThreadHandler(){
|
||||||
Timers.setDeltaProvider(() -> {
|
Timers.setDeltaProvider(() -> {
|
||||||
float result = Gdx.graphics.getDeltaTime() * 60f;
|
float result = Gdx.graphics.getDeltaTime() * 60f;
|
||||||
return Math.min(Float.isNaN(result) || Float.isInfinite(result) ? 1f : result, 15f);
|
return Float.isNaN(result) || Float.isInfinite(result) ? 1f : Math.min(result, 60f / 10f);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
|
|||||||
float x = snappedX(), y = snappedY();
|
float x = snappedX(), y = snappedY();
|
||||||
|
|
||||||
if(!movement.isZero() && moved && !state.isPaused()){
|
if(!movement.isZero() && moved && !state.isPaused()){
|
||||||
walktime += Timers.delta() * movement.len() / 0.7f * getFloorOn().speedMultiplier;
|
walktime += movement.len() / 0.7f * getFloorOn().speedMultiplier;
|
||||||
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
|
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,8 +554,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
|
|||||||
|
|
||||||
updateBuilding(this);
|
updateBuilding(this);
|
||||||
|
|
||||||
x = Mathf.clamp(x, 0, world.width() * tilesize);
|
x = Mathf.clamp(x, tilesize, world.width() * tilesize - tilesize);
|
||||||
y = Mathf.clamp(y, 0, world.height() * tilesize);
|
y = Mathf.clamp(y, tilesize, world.height() * tilesize - tilesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateMech(){
|
protected void updateMech(){
|
||||||
@@ -612,11 +612,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
|
|||||||
pointerY = vec.y;
|
pointerY = vec.y;
|
||||||
updateShooting();
|
updateShooting();
|
||||||
|
|
||||||
movement.limit(speed * Timers.delta());
|
movement.limit(speed).scl(Timers.delta());
|
||||||
|
|
||||||
if(getCarrier() == null){
|
if(getCarrier() == null){
|
||||||
if(!ui.chatfrag.chatOpen()){
|
if(!ui.chatfrag.chatOpen()){
|
||||||
velocity.add(movement);
|
velocity.add(movement.x, movement.y);
|
||||||
}
|
}
|
||||||
float prex = x, prey = y;
|
float prex = x, prey = y;
|
||||||
updateVelocityStatus();
|
updateVelocityStatus();
|
||||||
@@ -813,6 +813,12 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
|
|||||||
|
|
||||||
//region read and write methods
|
//region read and write methods
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClipped(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSave(DataOutput stream) throws IOException{
|
public void writeSave(DataOutput stream) throws IOException{
|
||||||
stream.writeBoolean(isLocal);
|
stream.writeBoolean(isLocal);
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
|||||||
|
|
||||||
status.update(this);
|
status.update(this);
|
||||||
|
|
||||||
velocity.limit(getMaxVelocity()).scl(status.getSpeedMultiplier());
|
velocity.limit(getMaxVelocity()).scl(1f + (status.getSpeedMultiplier()-1f) * Timers.delta());
|
||||||
|
|
||||||
if(isFlying()){
|
if(isFlying()){
|
||||||
x += velocity.x * Timers.delta();
|
x += velocity.x * Timers.delta();
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package io.anuke.mindustry.entities.traits;
|
package io.anuke.mindustry.entities.traits;
|
||||||
|
|
||||||
|
import io.anuke.mindustry.core.NetClient;
|
||||||
import io.anuke.mindustry.net.Interpolator;
|
import io.anuke.mindustry.net.Interpolator;
|
||||||
|
import io.anuke.ucore.core.Core;
|
||||||
import io.anuke.ucore.entities.trait.Entity;
|
import io.anuke.ucore.entities.trait.Entity;
|
||||||
|
import io.anuke.ucore.util.Tmp;
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
@@ -24,8 +27,21 @@ public interface SyncTrait extends Entity, TypeTrait{
|
|||||||
|
|
||||||
/**Interpolate entity position only. Override if you need to interpolate rotations or other values.*/
|
/**Interpolate entity position only. Override if you need to interpolate rotations or other values.*/
|
||||||
default void interpolate(){
|
default void interpolate(){
|
||||||
if(getInterpolator() == null)
|
if(getInterpolator() == null){
|
||||||
throw new RuntimeException("This entity must have an interpolator to interpolate()!");
|
throw new RuntimeException("This entity must have an interpolator to interpolate()!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isClipped()){
|
||||||
|
//move off screen when no longer in bounds
|
||||||
|
Tmp.r1.setSize(Core.camera.viewportWidth * Core.camera.zoom * NetClient.viewScale,
|
||||||
|
Core.camera.viewportHeight * Core.camera.zoom * NetClient.viewScale)
|
||||||
|
.setCenter(Core.camera.position.x, Core.camera.position.y);
|
||||||
|
|
||||||
|
if(!Tmp.r1.contains(getX(), getY()) && !Tmp.r1.contains(getInterpolator().last.x, getInterpolator().last.y)){
|
||||||
|
set(-99999f, -99999f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getInterpolator().update();
|
getInterpolator().update();
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ public abstract class GroundUnit extends BaseUnit{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void patrol(){
|
protected void patrol(){
|
||||||
vec.trns(baseRotation, type.speed);
|
vec.trns(baseRotation, type.speed * Timers.delta());
|
||||||
velocity.add(vec.x, vec.y);
|
velocity.add(vec.x, vec.y);
|
||||||
vec.trns(baseRotation, type.hitsizeTile);
|
vec.trns(baseRotation, type.hitsizeTile);
|
||||||
Tile tile = world.tileWorld(x + vec.x, y + vec.y);
|
Tile tile = world.tileWorld(x + vec.x, y + vec.y);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class MinimapRenderer implements Disposable{
|
|||||||
for(Unit unit : units){
|
for(Unit unit : units){
|
||||||
float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h;
|
float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h;
|
||||||
Draw.color(unit.getTeam().color);
|
Draw.color(unit.getTeam().color);
|
||||||
Draw.rect("white", x + rx, y + ry, w / (sz * 2), h / (sz * 2));
|
Draw.crect(Draw.getBlankRegion(), x + rx, y + ry, w / (sz * 2), h / (sz * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
Draw.color();
|
Draw.color();
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
package io.anuke.mindustry.maps.generation.pathfinding;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.math.GridPoint2;
|
|
||||||
import com.badlogic.gdx.math.MathUtils;
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
|
||||||
import com.badlogic.gdx.utils.Queue;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
import io.anuke.ucore.function.Predicate;
|
|
||||||
import io.anuke.ucore.util.Geometry;
|
|
||||||
|
|
||||||
public class FlowPathFinder extends TilePathfinder{
|
|
||||||
protected float[][] weights;
|
|
||||||
|
|
||||||
public FlowPathFinder(Tile[][] tiles){
|
|
||||||
super(tiles);
|
|
||||||
this.weights = new float[tiles.length][tiles[0].length];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void search(Tile start, Tile end, Array<Tile> out){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void search(Tile start, Predicate<Tile> result, Array<Tile> out){
|
|
||||||
Queue<Tile> queue = new Queue<>();
|
|
||||||
|
|
||||||
for(int i = 0; i < weights.length; i++){
|
|
||||||
for(int j = 0; j < weights[0].length; j++){
|
|
||||||
if(result.test(tiles[i][j])){
|
|
||||||
weights[i][j] = 100000;
|
|
||||||
queue.addLast(tiles[i][j]);
|
|
||||||
}else{
|
|
||||||
weights[i][j] = 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(queue.size > 0){
|
|
||||||
Tile tile = queue.first();
|
|
||||||
for(GridPoint2 point : Geometry.d4){
|
|
||||||
int nx = tile.x + point.x, ny = tile.y + point.y;
|
|
||||||
if(inBounds(nx, ny) && weights[nx][ny] < weights[tile.x][tile.y] - 1f && tiles[nx][ny].passable()){
|
|
||||||
weights[nx][ny] = weights[tile.x][tile.y] - 1;
|
|
||||||
queue.addLast(tiles[nx][ny]);
|
|
||||||
if(result.test(tiles[nx][ny])){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.add(start);
|
|
||||||
while(true){
|
|
||||||
Tile tile = out.peek();
|
|
||||||
|
|
||||||
Tile max = null;
|
|
||||||
float maxf = weights[tile.x][tile.y];
|
|
||||||
for(GridPoint2 point : Geometry.d4){
|
|
||||||
int nx = tile.x + point.x, ny = tile.y + point.y;
|
|
||||||
if(inBounds(nx, ny) && (weights[nx][ny] > maxf)){
|
|
||||||
max = tiles[nx][ny];
|
|
||||||
maxf = weights[nx][ny];
|
|
||||||
|
|
||||||
if(MathUtils.isEqual(maxf, 100000)){
|
|
||||||
out.add(max);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(max == null){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out.add(max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package io.anuke.mindustry.maps.generation.pathfinding;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
import io.anuke.ucore.util.Structs;
|
|
||||||
|
|
||||||
public abstract class TilePathfinder{
|
|
||||||
protected Tile[][] tiles;
|
|
||||||
|
|
||||||
public TilePathfinder(Tile[][] tiles){
|
|
||||||
this.tiles = tiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean inBounds(int x, int y){
|
|
||||||
return Structs.inBounds(x, y, tiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void search(Tile start, Tile end, Array<Tile> out);
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
package io.anuke.mindustry.maps.missions;
|
package io.anuke.mindustry.maps.missions;
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import io.anuke.mindustry.Vars;
|
|
||||||
import io.anuke.mindustry.game.GameMode;
|
import io.anuke.mindustry.game.GameMode;
|
||||||
import io.anuke.mindustry.game.SpawnGroup;
|
import io.anuke.mindustry.game.SpawnGroup;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.Team;
|
||||||
import io.anuke.mindustry.game.Waves;
|
import io.anuke.mindustry.game.Waves;
|
||||||
import io.anuke.mindustry.maps.Sector;
|
import io.anuke.mindustry.maps.Sector;
|
||||||
import io.anuke.mindustry.maps.generation.Generation;
|
import io.anuke.mindustry.maps.generation.Generation;
|
||||||
import io.anuke.mindustry.net.Net;
|
|
||||||
import io.anuke.ucore.util.Bundles;
|
import io.anuke.ucore.util.Bundles;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
@@ -62,9 +60,9 @@ public class WaveMission extends MissionWithStartingCore{
|
|||||||
public String displayString(){
|
public String displayString(){
|
||||||
return state.wave > target ?
|
return state.wave > target ?
|
||||||
Bundles.format(
|
Bundles.format(
|
||||||
Vars.unitGroups[Vars.waveTeam.ordinal()].size() > 1 && !Net.client() ?
|
state.enemies() > 1 ?
|
||||||
"text.mission.wave.enemies" :
|
"text.mission.wave.enemies" :
|
||||||
"text.mission.wave.enemy", target, target, Vars.unitGroups[Vars.waveTeam.ordinal()].size()) :
|
"text.mission.wave.enemy", target, target, state.enemies()) :
|
||||||
Bundles.format("text.mission.wave", state.wave, target, (int)(state.wavetime/60));
|
Bundles.format("text.mission.wave", state.wave, target, (int)(state.wavetime/60));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +80,6 @@ public class WaveMission extends MissionWithStartingCore{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isComplete(){
|
public boolean isComplete(){
|
||||||
return state.wave > target && Vars.unitGroups[Vars.waveTeam.ordinal()].size() == 0;
|
return state.wave > target && state.enemies() == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -366,10 +366,10 @@ public class HudFragment extends Fragment{
|
|||||||
|
|
||||||
table.labelWrap(() ->
|
table.labelWrap(() ->
|
||||||
world.getSector() == null ?
|
world.getSector() == null ?
|
||||||
(unitGroups[waveTeam.ordinal()].size() > 0 && state.mode.disableWaveTimer ?
|
(state.enemies() > 0 && state.mode.disableWaveTimer ?
|
||||||
wavef.get(state.wave) + "\n" + (unitGroups[waveTeam.ordinal()].size() == 1 ?
|
wavef.get(state.wave) + "\n" + (state.enemies() == 1 ?
|
||||||
enemyf.get(unitGroups[waveTeam.ordinal()].size()) :
|
enemyf.get(state.enemies()) :
|
||||||
enemiesf.get(unitGroups[waveTeam.ordinal()].size())) :
|
enemiesf.get(state.enemies())) :
|
||||||
wavef.get(state.wave) + "\n" +
|
wavef.get(state.wave) + "\n" +
|
||||||
(!state.mode.disableWaveTimer ?
|
(!state.mode.disableWaveTimer ?
|
||||||
Bundles.format("text.wave.waiting", (int)(state.wavetime/60)) :
|
Bundles.format("text.wave.waiting", (int)(state.wavetime/60)) :
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import io.anuke.mindustry.world.Tile;
|
|||||||
import io.anuke.mindustry.world.blocks.SelectionTrait;
|
import io.anuke.mindustry.world.blocks.SelectionTrait;
|
||||||
import io.anuke.ucore.graphics.Draw;
|
import io.anuke.ucore.graphics.Draw;
|
||||||
import io.anuke.ucore.scene.ui.layout.Table;
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
import io.anuke.ucore.util.Log;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import static io.anuke.mindustry.Vars.*;
|
|
||||||
|
import static io.anuke.mindustry.Vars.content;
|
||||||
|
|
||||||
public class SortedUnloader extends Unloader implements SelectionTrait{
|
public class SortedUnloader extends Unloader implements SelectionTrait{
|
||||||
protected float speed = 1f;
|
protected float speed = 1f;
|
||||||
@@ -38,7 +38,6 @@ public class SortedUnloader extends Unloader implements SelectionTrait{
|
|||||||
SortedUnloaderEntity entity = tile.entity();
|
SortedUnloaderEntity entity = tile.entity();
|
||||||
|
|
||||||
if(tile.entity.timer.get(timerUnload, speed) && tile.entity.items.total() == 0){
|
if(tile.entity.timer.get(timerUnload, speed) && tile.entity.items.total() == 0){
|
||||||
Log.info(threads.getFrameID());
|
|
||||||
for(Tile other : tile.entity.proximity()){
|
for(Tile other : tile.entity.proximity()){
|
||||||
if(other.getTeam() == tile.getTeam() && other.block() instanceof StorageBlock && entity.items.total() == 0 &&
|
if(other.getTeam() == tile.getTeam() && other.block() instanceof StorageBlock && entity.items.total() == 0 &&
|
||||||
((entity.sortItem == null && other.entity.items.total() > 0) || ((StorageBlock) other.block()).hasItem(other, entity.sortItem))){
|
((entity.sortItem == null && other.entity.items.total() > 0) || ((StorageBlock) other.block()).hasItem(other, entity.sortItem))){
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class UnitFactory extends Block{
|
|||||||
if(!Net.client()){
|
if(!Net.client()){
|
||||||
BaseUnit unit = factory.type.create(tile.getTeam());
|
BaseUnit unit = factory.type.create(tile.getTeam());
|
||||||
unit.setSpawner(tile);
|
unit.setSpawner(tile);
|
||||||
unit.set(tile.drawx(), tile.drawy());
|
unit.set(tile.drawx() + Mathf.range(4), tile.drawy() + Mathf.range(4));
|
||||||
unit.add();
|
unit.add();
|
||||||
unit.getVelocity().y = factory.launchVelocity;
|
unit.getVelocity().y = factory.launchVelocity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ public class PowerModule extends BlockModule{
|
|||||||
if(Float.isNaN(amount)){
|
if(Float.isNaN(amount)){
|
||||||
amount = 0f;
|
amount = 0f;
|
||||||
}
|
}
|
||||||
|
// Workaround: If power went negative for some reason, at least fix it when reloading the map
|
||||||
|
if(amount < 0f){
|
||||||
|
amount = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
short amount = stream.readShort();
|
short amount = stream.readShort();
|
||||||
for(int i = 0; i < amount; i++){
|
for(int i = 0; i < amount; i++){
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import io.anuke.mindustry.core.Platform;
|
|||||||
import io.anuke.mindustry.game.Saves.SaveSlot;
|
import io.anuke.mindustry.game.Saves.SaveSlot;
|
||||||
import io.anuke.mindustry.io.SaveIO;
|
import io.anuke.mindustry.io.SaveIO;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
|
||||||
import io.anuke.ucore.scene.ui.layout.Unit;
|
import io.anuke.ucore.scene.ui.layout.Unit;
|
||||||
import io.anuke.ucore.util.Bundles;
|
import io.anuke.ucore.util.Bundles;
|
||||||
import io.anuke.ucore.util.Strings;
|
import io.anuke.ucore.util.Strings;
|
||||||
@@ -40,16 +39,6 @@ public class IOSLauncher extends IOSApplication.Delegate {
|
|||||||
|
|
||||||
Platform.instance = new Platform() {
|
Platform.instance = new Platform() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDialog(TextField field) {
|
|
||||||
TextFieldDialogListener.add(field, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDialog(TextField field, int maxLength) {
|
|
||||||
TextFieldDialogListener.add(field, maxLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shareFile(FileHandle file){
|
public void shareFile(FileHandle file){
|
||||||
FileHandle to = Gdx.files.absolute(getDocumentsDirectory()).child(file.name());
|
FileHandle to = Gdx.files.absolute(getDocumentsDirectory()).child(file.name());
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
package io.anuke.mindustry;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import io.anuke.ucore.scene.event.ClickListener;
|
|
||||||
import io.anuke.ucore.scene.event.InputEvent;
|
|
||||||
import io.anuke.ucore.scene.event.InputListener;
|
|
||||||
import io.anuke.ucore.scene.ui.TextField;
|
|
||||||
import org.robovm.apple.foundation.NSRange;
|
|
||||||
import org.robovm.apple.uikit.*;
|
|
||||||
import org.robovm.rt.bro.annotation.ByVal;
|
|
||||||
|
|
||||||
public class TextFieldDialogListener {
|
|
||||||
|
|
||||||
public static void add(TextField field, int maxLength){
|
|
||||||
field.addListener(new ClickListener(){
|
|
||||||
public void clicked(final InputEvent event, float x, float y){
|
|
||||||
show(field, maxLength);
|
|
||||||
event.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
field.addListener(new InputListener(){
|
|
||||||
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
|
|
||||||
Gdx.input.setOnscreenKeyboardVisible(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private static void show(TextField field, int maxLength){
|
|
||||||
|
|
||||||
UIAlertViewDelegateAdapter delegate = new UIAlertViewDelegateAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void didDismiss(UIAlertView alertView, long buttonIndex) {
|
|
||||||
if (buttonIndex == 1) {
|
|
||||||
UITextField textField = alertView.getTextField(0);
|
|
||||||
final String result = textField.getText();
|
|
||||||
|
|
||||||
Gdx.app.postRunnable(() -> {
|
|
||||||
field.setText(result);
|
|
||||||
field.change();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clicked(UIAlertView alertView, long buttonIndex) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel(UIAlertView alertView) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void willPresent(UIAlertView alertView) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void didPresent(UIAlertView alertView) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void willDismiss(UIAlertView alertView, long buttonIndex) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableFirstOtherButton(UIAlertView alertView) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
String[] otherButtons = new String[1];
|
|
||||||
otherButtons[0] = "OK";
|
|
||||||
|
|
||||||
UIAlertView alertView = new UIAlertView("", "", delegate, "Cancel", otherButtons);
|
|
||||||
|
|
||||||
alertView.setAlertViewStyle(UIAlertViewStyle.PlainTextInput);
|
|
||||||
|
|
||||||
UITextField uiTextField = alertView.getTextField(0);
|
|
||||||
uiTextField.setText(field.getText());
|
|
||||||
|
|
||||||
uiTextField.setDelegate(new UITextFieldDelegateAdapter() {
|
|
||||||
@Override
|
|
||||||
public boolean shouldChangeCharacters(UITextField textField, @ByVal NSRange nsRange, String additionalText) {
|
|
||||||
|
|
||||||
if (textField.getText().length() + additionalText.length() > maxLength) {
|
|
||||||
String oldText = textField.getText();
|
|
||||||
String newText = oldText + additionalText;
|
|
||||||
textField.setText(newText.substring(0, maxLength));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
alertView.show();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,8 @@ import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
|||||||
import io.anuke.kryonet.KryoClient;
|
import io.anuke.kryonet.KryoClient;
|
||||||
import io.anuke.kryonet.KryoServer;
|
import io.anuke.kryonet.KryoServer;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.ucore.core.Settings;
|
||||||
import io.anuke.ucore.util.EmptyLogger;
|
import io.anuke.ucore.util.EmptyLogger;
|
||||||
import io.anuke.ucore.util.OS;
|
|
||||||
|
|
||||||
public class ServerLauncher extends HeadlessApplication{
|
public class ServerLauncher extends HeadlessApplication{
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ public class ServerLauncher extends HeadlessApplication{
|
|||||||
Net.setServerProvider(new KryoServer());
|
Net.setServerProvider(new KryoServer());
|
||||||
|
|
||||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||||
config.preferencesDirectory = OS.getAppDataDirectoryString("Mindustry");
|
Settings.setPrefHandler((appName) -> Gdx.files.local("config"));
|
||||||
|
|
||||||
new ServerLauncher(new MindustryServer(args), config);
|
new ServerLauncher(new MindustryServer(args), config);
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
|
|||||||
Reference in New Issue
Block a user