diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 3767dee4fe..5749131584 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -427,7 +427,7 @@ item.titanium.name = Titan item.titanium.description = Ein seltenes sehr leichtes Metal. Häufig in Flüssigkeits-Transport Blöcken, Abbauanlagen und Flugzeugen verwendet. item.thorium.name = Uran item.thorium.description = Ein dichtes radioaktives Metal, welches als strukturelle Unterstützung und nuklearer Kraftstoff verwendet wird. -item.silicon.name = Silikon +item.silicon.name = Silizium item.silicon.description = Ein sehr nützlicher Halbleiter. Findet Anwendung in Solar Anlagen und komplexer Elektronik. item.plastanium.name = Plastanium item.plastanium.description = Ein leichtes dehnbares Material welches in Flugzeugen und Splittermunition verwendet wird. @@ -536,7 +536,7 @@ block.bridgeconveyor.name = Transportband Brücke block.bridgeconveyor.description = Ein Transportband welches über Blöcke gehen kann. Insgesamt maximal zwei Blöcke hoch. block.smelter.name = Schmelzer block.arc-smelter.name = Lichtbogen Schmelzer -block.silicon-smelter.name = Silikon Schmelzer +block.silicon-smelter.name = Silizium Schmelzer block.phase-weaver.name = Phase Weaver block.pulverizer.name = Pulverisierer block.cryofluidmixer.name = Cryofluid Mixer @@ -656,13 +656,13 @@ tutorial.waves = Der [LIGHT_GRAY] Gegner[] greift an.\n\nVerteidige deinen Kern tutorial.lead = Mehr Erz ist verfügbar. Finde Blei und bau es ab.\n\n Klicke auf deine Einheit und ziehe die Maus auf den Kern um Ressourcen zu übertragen. tutorial.smelter = Kupfer und Blei sind schwache Metalle.\n Super [accent]dichte Legierung [] kann in einem Schmeltzer erzeugt werden.\n\n Bau einen. tutorial.densealloy = Der Schmeltzer wird nun Legierung produzieren.\n Produziere einige.\n Verbessere die Produktion sofern notwendig. -tutorial.siliconsmelter = Der Kern wird nun [accent]spirit drohnen[] erstellen. Diese Bauen Rohstoffe und reparieren Blöcke.\n\nFabriken für andere Einheiten benötigen [accent]Silikon[].\n Baue ein Silikon Schmeltzer. -tutorial.silicondrill = Silikon benötigt [accent]Kohle[] und [accent]Sand[].\n Fange damit an die Bohrer zu platzieren. +tutorial.siliconsmelter = Der Kern wird nun [accent]spirit drohnen[] erstellen. Diese Bauen Rohstoffe und reparieren Blöcke.\n\nFabriken für andere Einheiten benötigen [accent]Silizium[].\n Baue ein Silizium Schmeltzer. +tutorial.silicondrill = Silizium benötigt [accent]Kohle[] und [accent]Sand[].\n Fange damit an die Bohrer zu platzieren. tutorial.generator = Diese Technologie benötigt power.\n Erstelle einen Verbrennungs-Generator dafür. tutorial.generatordrill = Verbrennungs Generatoren benötigen Kraftstoff.\nBenutze Kohle aus einem Bohrer als Kraftstoff. tutorial.node = Power muss transportiert werden.\nErstelle einen [accent]Power Knoten[] nahe deinem Verbrennungs Generator um seine Power zu transportieren. -tutorial.nodelink = Power kann über verbundene Power Blocks, Generatoren oder Power Knoten transferierd werden.\n\n Verbinde die Power in dem du auf den Knoten klickst und dann den Generator und den Silikon Schmeltzer auswählst. -tutorial.silicon = Silikon wird produziert. Produziere einiges.\n\n Verbesserungen am Produktionssystem werden empfohlen. +tutorial.nodelink = Power kann über verbundene Power Blocks, Generatoren oder Power Knoten transferierd werden.\n\n Verbinde die Power in dem du auf den Knoten klickst und dann den Generator und den Silizium Schmeltzer auswählst. +tutorial.silicon = Silizium wird produziert. Produziere einiges.\n\n Verbesserungen am Produktionssystem werden empfohlen. tutorial.daggerfactory = Konstruiere eine Dagger Mech Fabrik.\n\n Diese wird verwendet um Angreifende Mechs zu erstellen. tutorial.router = Fabriken benötigen Ressourcen um zu funktionieren.\n Platziere ein Router um Gegenstände auf Transportbändern aufzuteilen. tutorial.dagger = Verbinde die Fabrik mit einem Power Knoten. Wenn alle Voraussetzungen gegeben sind, beginnt die Fabrik Mechs zu konstruieren.\n\n Platziere mehr Bohrer und Transportbänder um die Versorgung der Fabrik zu sichern. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 54bcbf9ad3..ee0c50c2e1 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -1,7 +1,7 @@ text.credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet\!) text.credits = Crédits text.discord = Rejoignez le discord de Mindustry -text.link.discord.description = the official Mindustry discord chatroom +text.link.discord.description = Le discord officiel de mindustry text.link.github.description = Code source du jeu text.link.dev-builds.description = Versions instables du jeu text.link.trello.description = Trello officiel pour les futurs ajouts . @@ -14,12 +14,12 @@ text.web.unsupported = La version web ne supporte pas cette fonction \! Téléch text.gameover = Partie terminée. text.gameover.pvp = L'équipe [accent] {0}[] a gagnée \! text.sector.gameover = Ce secteur a été perdu. Réessayer? -text.sector.retry = Retry +text.sector.retry = Réessayer text.highscore = [YELLOW]Nouveau meilleur score\! -text.wave.lasted = You lasted until wave [accent]{0}[]. +text.wave.lasted = Vous avez survécu jusqu'à la vague [accent]{0}[]. text.level.highscore = Meilleur score\: [accent]{0} text.level.delete.title = Confirmer -text.map.delete = Are you sure you want to delete the map "[orange]{0}[]"? +text.map.delete = Êtes-vous sûr de supprimer cette carte"[orange]{0}[]"? text.level.select = Sélection de niveau text.level.mode = Mode de jeu \: text.construction.desktop = Pour désélectionner un bloc ou arrêter de construire, appuyer sur [accent]espace[]. @@ -308,7 +308,7 @@ text.blocks.outputitem = Objet produit text.blocks.drilltier = Forable text.blocks.drillspeed = Vitesse de forage de base text.blocks.liquidoutput = Liquide en sortie -text.blocks.liquidoutputspeed = Liquid Output Speed +text.blocks.liquidoutputspeed = Vitesse de production de liquide text.blocks.liquiduse = Quantité de liquide utilisée text.blocks.coolant = Liquide de refroidissement text.blocks.coolantuse = Quantité de liquide de refroidissement utilisée @@ -321,8 +321,8 @@ text.blocks.shots = Tir text.blocks.reload = Tirs/Seconde text.blocks.inputfuel = Carburant text.blocks.fuelburntime = Durée du carburant -text.blocks.inputcapacity = Input capacity -text.blocks.outputcapacity = Output capacity +text.blocks.inputcapacity = Capacité d'entrée +text.blocks.outputcapacity = Capacité de production text.unit.blocks = blocs text.unit.powersecond = Énergie/seconde text.unit.liquidsecond = Liquides/seconde @@ -369,31 +369,31 @@ setting.sfxvol.name = Volume des SFX setting.mutesound.name = Couper les SFX text.keybind.title = Paramétrer les touches category.general.name = General -category.view.name = View -category.multiplayer.name = Multiplayer -command.attack = Attack -command.retreat = Retreat -command.patrol = Patrol -keybind.press = Press a key... -keybind.press.axis = Press an axis or key... +category.view.name = Voir +category.multiplayer.name = Multijoueur +command.attack = Attaque +command.retreat = Retraite +command.patrol = Patrouille +keybind.press = Appuyer sur une touche... +keybind.press.axis = Appuyer sur un axe ou une touche... keybind.move_x.name = mouvement x keybind.move_y.name = mouvement y keybind.select.name = sélectionner keybind.break.name = Pause -keybind.deselect.name = Deselect +keybind.deselect.name = Déselectionner keybind.shoot.name = tirer keybind.zoom_hold.name = tenir le zoom keybind.zoom.name = zoom keybind.menu.name = menu keybind.pause.name = Pause -keybind.dash.name = sprint +keybind.dash.name = Courir keybind.chat.name = chat keybind.player_list.name = Liste des joueurs keybind.console.name = console keybind.rotate.name = Tourner -keybind.toggle_menus.name = Toggle menus -keybind.chat_history_prev.name = Chat history prev -keybind.chat_history_next.name = Chat history next +keybind.toggle_menus.name = Cacher/afficher les menus +keybind.chat_history_prev.name = remonter l'historique du chat +keybind.chat_history_next.name = descendre l'historique du chat keybind.chat_scroll.name = Chat scroll keybind.drop_unit.name = drop unit keybind.zoom_minimap.name = Zoom minimap @@ -403,7 +403,7 @@ mode.waves.description = le mode de jeu normal. Ressource limitée et vagues d'e mode.sandbox.name = bac à sable mode.sandbox.description = Ressources infinies et pas de timer pour les vagues. mode.custom.warning = Notez que les blocs débloqués en partie personnalisées ne sont pas conservés pour les secteurs.\n\n[LIGHT_GRAY]En mode bac à sable, seul les blocs débloqués en mode secteur peuvent être utilisés. -mode.custom.warning.read = Just to make sure you've read it\:\n[scarlet]UNLOCKS IN CUSTOM GAMES DO NOT CARRY OVER TO SECTORS OR OTHER MODES\!\n\n[LIGHT_GRAY](I wish this wasn't necessary, but apparently it is) +mode.custom.warning.read = Simplement pour vérifier que vous l'avez lu \:\n[scarlet]CE QUI EST DEBLOQUE LORS DES PARITES PERSONNALISEES NE L'EST POUR LES SECTEURS OU LES AUTRES MODES DE JEU\!\n\n[LIGHT_GRAY](J'aurais souhaité que ce ne soit pas nécessaire, mais ça a l'air de l'être ) mode.freebuild.name = construction libre mode.freebuild.description = Ressource limitée et pas de timer pour les vagues. mode.pvp.name = JcJ @@ -427,14 +427,14 @@ item.titanium.name = Titane item.titanium.description = Un métal rare super-léger largement utilisé dans le transport de liquides et d'objets ainsi que dans les foreuses de haut-niveau et l'aviation .item.thorium.name\=Thorium item.thorium.name = Thorium item.thorium.description = Un métal dense, et radioactif utilisé comme support structurel et comme carburant nucléaire. -item.silicon.name = Silicon -item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.silicon.name = Silicone +item.silicon.description=Un matériau semi-conducteur extrêmement utile, avec des utilisations dans les panneaux solaires et beaucoup d'autre composants électroniques complexes. item.plastanium.name = Plastanium item.plastanium.description = Un matériau léger et docile utilisé dans l'aviation avancée et dans les munitions à fragmentation. item.phase-matter.name = Matière phasée item.surge-alloy.name = alliage superchargé item.biomatter.name = Biomasse -item.biomatter.description = A clump of organic mush; used for conversion into oil or as a basic fuel. +item.biomatter.description = Un mélange de matières organiques; utilisé pour la transformation en huile ou en tant que carburant de base. item.sand.name = Sable item.sand.description = Un matériau commun utilisé largement dans la fonte, à la fois dans l'alliage et comme un flux. item.blast-compound.name = Mélange explosif @@ -528,13 +528,13 @@ block.router.name = [accent]routeur[] block.router.description = Distribue les articles dans les 4 directions. Le seul et l'unique. block.distributor.name = Distributeur block.distributor.description = C'est un bloc qui peut envoyer les articles dans 8 directions. -block.sorter.name = Sorter +block.sorter.name = Sorteur block.sorter.description = Trie les articles. Si un article rcorrespond à la sélection, il peut passer. Autrement, l'article est distribué vers la gauche ou la droite. block.overflow-gate.name = Barrière de Débordement block.overflow-gate.description = C'est la combinaison entre un Routeur et un Diviseur qui peut seulement distribuer à gauche et à droite si le chemin de devant est bloqué. block.bridgeconveyor.name = Pont block.bridgeconveyor.description = C'est un convoyeur qui peut passer par-dessus les blocs, jusqu'à deux blocs de distance. -block.smelter.name = Smelter +block.smelter.name = Fonderie d'alliage lourd block.arc-smelter.name = Fonderie d'alliage lourd électrique block.silicon-smelter.name = Fonderie de Silicone block.phase-weaver.name = Tisseur à Phase @@ -593,7 +593,7 @@ block.wraith-factory.name = Usine de "Combattants spectraux" block.ghoul-factory.name = Usine de "Bombardiers goules" block.dagger-factory.name = Usine de "Poignards" block.titan-factory.name = Usine de "Titans" -block.fortress-factory.name = Fortress Mech Factory +block.fortress-factory.name = Usine de "Forteresse" block.revenant-factory.name = Usine de "Revenants" block.repair-point.name = Point de Réparation block.pulse-conduit.name = Conduit à Impulsion @@ -634,10 +634,10 @@ unit.spirit.name = Drone sppirituel unit.spirit.description = L'unité de soutien de départ.Apparaît dans la base par défaut .Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. unit.phantom.name = Drone Fantôme unit.phantom.description = Une unité de soutien avancée. Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. Bien plus efficace qu'un drone spirituel. -unit.dagger.name = Dagger -unit.dagger.description = A basic ground unit. Useful in swarms. +unit.dagger.name = Poignard +unit.dagger.description = Une unité terrestre basiquee. Utile en armée. unit.titan.name = Titan -unit.titan.description = An advanced, armored ground unit. Attacks both ground and air targets. +unit.titan.description = Une unité terrestre cuirassée avancée. Attaque les unités terrestres comme aériennes. unit.ghoul.name = Bombardier goule unit.ghoul.description = Un bombardier lourd . Utilise de la pyratite ou des explosifs comme munitions. unit.wraith.name = Combattant spectral @@ -667,4 +667,4 @@ tutorial.daggerfactory = Construire [accent]une usine de "Poignards" []est recom tutorial.router = Les usines ont besoin de ressources pour fonctionner.\nCréez un routeur pour séparer les objets. tutorial.dagger = Reliez des transmetteurs énergétiques à l'usine.\nUne fois que les conditions seront remplies , un mécha sera créé.\nConstruisez autant de foreuses, de générateurs et de tapis roulants que nécessaire. tutorial.battle = [LIGHT_GRAY]L'Ennemi[] a révélé sa base .\nDétruisez la avec votre unité et des méchas "Poignard". -block.bridge-conveyor.description = C'est un convoyeur qui peut passer par-dessus les blocs, jusqu'à deux blocs de distance. \ No newline at end of file +block.bridge-conveyor.description = C'est un convoyeur qui peut passer par-dessus les blocs, jusqu'à deux blocs de distance. diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 361df5c93e..218bc80941 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -32,10 +32,10 @@ text.coreattack = < 코어가 공격받고 있습니다 ! > text.unlocks = 아이템들 text.savegame = 게임 저장 text.loadgame = 게임 불러오기 -text.joingame = 게임 참가 +text.joingame = 멀티플레이 text.addplayers = 플레이어 추가/제거 text.customgame = 커스텀 게임 -text.sectors = 구역 +text.sectors = 싱글 플레이 text.sector = 구역 : [LIGHT_GRAY]{0} text.sector.time = 시간 : [LIGHT_GRAY]{0} text.sector.deploy = 시작 @@ -280,8 +280,11 @@ text.paused = 일시 정지 text.yes = 예 text.no = 아니오 text.info.title = [accent]정보 + text.error.title = [crimson]오류가 발생했습니다. text.error.crashtitle = 오류가 발생했습니다. +text.error.alreadyconnected = 이미 접속중입니다. + text.blocks.blockinfo = 블록 정보 text.blocks.powercapacity = 최대 전력 용량 text.blocks.powershot = 1발당 전력 소모량 @@ -703,7 +706,7 @@ block.silicon-smelter.description = 실리콘을 제작할 수 있는 건물입 block.phase-weaver.description = 메타를 제작할 수 있는 건물입니다. block.pulverizer.description = 돌을 갈아서 모래로 만들 수 있는 건물입니다. block.cryofluidmixer.description = 냉각수를 제작할 수 있는 건물입니다. -block.melter.description = 용암을 돌로 만들 수 있는 건물입니다. +block.melter.description = 돌로 용암을 만들 수 있는 건물입니다. block.incinerator.description = 불필요한 아이템을 소각시켜 줄 수 있는 건물입니다. block.biomattercompressor.description = 잔디밭에서 바이오메터를 추출할 수 있는 건물입니다. block.separator.description = 돌을 분해하여 각종 자원으로 재활용 할 수 있게 해 주는 건물입니다. diff --git a/core/src/io/anuke/mindustry/ai/BlockIndexer.java b/core/src/io/anuke/mindustry/ai/BlockIndexer.java index 7fdd7ada51..d8dc3ae8eb 100644 --- a/core/src/io/anuke/mindustry/ai/BlockIndexer.java +++ b/core/src/io/anuke/mindustry/ai/BlockIndexer.java @@ -2,7 +2,7 @@ package io.anuke.mindustry.ai; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.*; -import io.anuke.mindustry.content.Items; +import com.badlogic.gdx.utils.Bits; import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.game.EventType.TileChangeEvent; @@ -14,10 +14,7 @@ import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.ucore.core.Events; import io.anuke.ucore.function.Predicate; -import io.anuke.ucore.util.EnumSet; -import io.anuke.ucore.util.Geometry; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.ThreadArray; +import io.anuke.ucore.util.*; import static io.anuke.mindustry.Vars.*; @@ -33,12 +30,14 @@ public class BlockIndexer{ private final static int structQuadrantSize = 12; /**Set of all ores that are being scanned.*/ - private final ObjectSet scanOres = ObjectSet.with(Items.copper, Items.coal, Items.lead, Items.thorium, Items.titanium); + private final ObjectSet scanOres = new ObjectSet(){{addAll(Item.getAllOres());}}; private final ObjectSet itemSet = new ObjectSet<>(); /**Stores all ore quadtrants on the map.*/ private ObjectMap> ores; /**Tags all quadrants.*/ private Bits[] structQuadrants; + /**Stores all damaged tile entities by team.*/ + private ObjectSet[] damagedTiles = new ObjectSet[Team.all.length]; /**Maps teams to a map of flagged tiles by type.*/ private ObjectSet[][] flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length]; @@ -62,7 +61,9 @@ public class BlockIndexer{ }); Events.on(WorldLoadEvent.class, event -> { + damagedTiles = new ObjectSet[Team.all.length]; flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length]; + for(int i = 0; i < flagMap.length; i++){ for(int j = 0; j < BlockFlag.all.length; j++){ flagMap[i][j] = new ObjectSet<>(); @@ -79,7 +80,13 @@ public class BlockIndexer{ for(int x = 0; x < world.width(); x++){ for(int y = 0; y < world.height(); y++){ - process(world.tile(x, y)); + Tile tile = world.tileWorld(x, y); + + process(tile); + + if(tile.entity != null && tile.entity.healthf() < 0.9999f){ + notifyTileDamaged(tile.entity); + } } } @@ -97,6 +104,28 @@ public class BlockIndexer{ return flagMap[team.ordinal()]; } + /**Returns all damaged tiles by team.*/ + public ObjectSet getDamaged(Team team){ + returnArray.clear(); + + if(damagedTiles[team.ordinal()] == null){ + damagedTiles[team.ordinal()] = new ObjectSet<>(); + } + + ObjectSet set = damagedTiles[team.ordinal()]; + for(Tile tile : set){ + if(tile.entity == null || tile.entity.getTeam() != team || tile.entity.healthf() >= 0.9999f){ + returnArray.add(tile); + } + } + + for(Tile tile : returnArray){ + set.remove(tile); + } + + return set; + } + /**Get all allied blocks with a flag.*/ public ObjectSet getAllied(Team team, BlockFlag type){ return flagMap[team.ordinal()][type.ordinal()]; @@ -115,6 +144,15 @@ public class BlockIndexer{ return returnArray; } + public void notifyTileDamaged(TileEntity entity){ + if(damagedTiles[entity.getTeam().ordinal()] == null){ + damagedTiles[entity.getTeam().ordinal()] = new ObjectSet<>(); + } + + ObjectSet set = damagedTiles[entity.getTeam().ordinal()]; + set.add(entity.tile); + } + public TileEntity findTile(Team team, float x, float y, float range, Predicate pred){ TileEntity closest = null; float dst = 0; diff --git a/core/src/io/anuke/mindustry/entities/TileEntity.java b/core/src/io/anuke/mindustry/entities/TileEntity.java index cec34d962d..910d36d10f 100644 --- a/core/src/io/anuke/mindustry/entities/TileEntity.java +++ b/core/src/io/anuke/mindustry/entities/TileEntity.java @@ -143,10 +143,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{ public void damage(float damage){ if(dead) return; + float preHealth = health; + Call.onTileDamage(tile, health - tile.block().handleDamage(tile, damage)); if(health <= 0){ Call.onTileDestroyed(tile); + }else if(preHealth >= maxHealth() - 0.00001f && health < maxHealth()){ //when just damaged + world.indexer.notifyTileDamaged(this); } } diff --git a/core/src/io/anuke/mindustry/entities/Units.java b/core/src/io/anuke/mindustry/entities/Units.java index f8e271fc5b..e546b727cd 100644 --- a/core/src/io/anuke/mindustry/entities/Units.java +++ b/core/src/io/anuke/mindustry/entities/Units.java @@ -11,8 +11,9 @@ import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityQuery; import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Predicate; -import io.anuke.ucore.util.Threads; import io.anuke.ucore.util.EnumSet; +import io.anuke.ucore.util.Geometry; +import io.anuke.ucore.util.Threads; import static io.anuke.mindustry.Vars.*; @@ -125,6 +126,12 @@ public class Units{ return value[0]; } + /**Returns the neareset damaged tile.*/ + public static TileEntity findDamagedTile(Team team, float x, float y){ + Tile tile = Geometry.findClosest(x, y, world.indexer.getDamaged(team)); + return tile == null ? null : tile.entity; + } + /**Returns the neareset ally tile in a range.*/ public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate pred){ return world.indexer.findTile(team, x, y, range, pred); diff --git a/core/src/io/anuke/mindustry/entities/units/types/Drone.java b/core/src/io/anuke/mindustry/entities/units/types/Drone.java index 244f5ef455..ed5cc2c6fd 100644 --- a/core/src/io/anuke/mindustry/entities/units/types/Drone.java +++ b/core/src/io/anuke/mindustry/entities/units/types/Drone.java @@ -79,12 +79,14 @@ public class Drone extends FlyingUnit implements BuilderTrait{ } //if it's missing requirements, try and mine them - for(ItemStack stack : entity.recipe.requirements){ - if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){ - targetItem = stack.item; - getPlaceQueue().clear(); - setState(mine); - return; + if(entity.recipe != null){ + for(ItemStack stack : entity.recipe.requirements){ + if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){ + targetItem = stack.item; + getPlaceQueue().clear(); + setState(mine); + return; + } } } @@ -102,22 +104,19 @@ public class Drone extends FlyingUnit implements BuilderTrait{ } public void update(){ - if(target != null && (((TileEntity) target).health >= ((TileEntity) target).tile.block().health - || target.distanceTo(Drone.this) > discoverRange)){ - target = null; - } - if(target == null){ - retarget(() -> { - target = Units.findAllyTile(team, x, y, discoverRange, - tile -> tile.entity != null && tile.entity.health + 0.0001f < tile.block().health); + retarget(() -> { + target = Units.findDamagedTile(team, x, y); - if(target == null){ - setState(mine); - } - }); - }else if(target.distanceTo(Drone.this) > type.range){ - circle(type.range); + if(target == null){ + setState(mine); + } + }); + + if(target == null) return; + + if(target.distanceTo(Drone.this) > type.range){ + circle(type.range*0.9f); }else{ TileEntity entity = (TileEntity) target; entity.healBy(type.healSpeed * entity.tile.block().health / 100f * Timers.delta()); @@ -316,7 +315,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{ target = null; } - if(Net.client() && state.is(repair) && target instanceof TileEntity){ + if(Net.client() && state.is(repair) && target instanceof TileEntity && target.distanceTo(this) < type.range){ TileEntity entity = (TileEntity) target; entity.health += type.healSpeed * Timers.delta(); entity.health = Mathf.clamp(entity.health, 0, entity.tile.block().health); @@ -327,7 +326,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{ @Override protected void updateRotation(){ - if(target != null && (state.is(repair) || state.is(mine))){ + if(target != null && ((state.is(repair) && target.distanceTo(this) < type.range) || state.is(mine))){ rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f); }else{ rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f); @@ -353,7 +352,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{ TargetTrait entity = target; - if(entity instanceof TileEntity && state.is(repair)){ + if(entity instanceof TileEntity && state.is(repair) && target.distanceTo(this) < type.range){ float len = 5f; Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f)); Shapes.laser("beam", "beam-end", diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index 3c2921145e..84b1563b22 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -206,22 +206,12 @@ public abstract class InputHandler extends InputAdapter{ consumed = true; showedInventory = true; } - - if(tile.block().consumes.hasAny()){ - frag.consume.show(tile); - consumed = true; - showedConsume = true; - } } if(!showedInventory){ frag.inv.hide(); } - if(!showedConsume){ - frag.consume.hide(); - } - if(!consumed && player.isBuilding()){ player.clearBuilding(); recipe = null; @@ -231,9 +221,7 @@ public abstract class InputHandler extends InputAdapter{ return consumed; } - /** - * Tries to select the player to drop off items, returns true if successful. - */ + /**Tries to select the player to drop off items, returns true if successful.*/ boolean tryTapPlayer(float x, float y){ if(canTapPlayer(x, y)){ droppingItem = true; @@ -246,9 +234,7 @@ public abstract class InputHandler extends InputAdapter{ return Vector2.dst(x, y, player.x, player.y) <= playerSelectRange && player.inventory.hasItem(); } - /** - * Tries to begin mining a tile, returns true if successful. - */ + /**Tries to begin mining a tile, returns true if successful.*/ boolean tryBeginMine(Tile tile){ if(canMine(tile)){ //if a block is clicked twice, reset it diff --git a/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java index 8b46947740..4d9411c4df 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java @@ -6,10 +6,10 @@ import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.UnlockableContent; import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.scene.event.HandCursorListener; import io.anuke.ucore.scene.ui.Image; import io.anuke.ucore.scene.ui.ScrollPane; -import io.anuke.ucore.scene.ui.Tooltip; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.utils.UIUtils; @@ -65,10 +65,7 @@ public class UnlocksDialog extends FloatingDialog{ if(control.unlocks.isUnlocked(unlock)){ image.clicked(() -> Vars.ui.content.show(unlock)); - image.addListener(new Tooltip<>(new Table("clear"){{ - add(unlock.localizedName()); - margin(4); - }})); + StatValue.addToolTip(image, unlock); } if((++count) % maxWidth == 0){ diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlockConsumeFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlockConsumeFragment.java index 0e9f928803..1fbd83d39b 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlockConsumeFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlockConsumeFragment.java @@ -12,6 +12,7 @@ import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.consumers.Consume; import io.anuke.ucore.core.Graphics; +import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Group; import io.anuke.ucore.scene.ui.layout.Table; @@ -19,6 +20,7 @@ import static io.anuke.mindustry.Vars.*; public class BlockConsumeFragment extends Fragment{ private Table table; + private Tile lastTile; private boolean visible; @Override @@ -26,6 +28,24 @@ public class BlockConsumeFragment extends Fragment{ table = new Table(); table.visible(() -> !state.is(State.menu) && visible); table.setTransform(true); + + parent.addChild(new Element(){{update(() -> { + if(!ui.hasMouse()){ + Tile tile = world.tileWorld(Graphics.mouseWorld().x, Graphics.mouseWorld().y); + if(tile == null) return; + tile = tile.target(); + + if(tile != lastTile){ + if(tile.block().consumes.hasAny()){ + show(tile); + }else if(visible){ + hide(); + } + lastTile = tile; + } + } + });}}); + parent.setTransform(true); parent.addChild(table); } @@ -66,7 +86,7 @@ public class BlockConsumeFragment extends Fragment{ rebuild(block, entity); } - Vector2 v = Graphics.screen(tile.drawx() - tile.block().size * tilesize / 2f, tile.drawy() + tile.block().size * tilesize / 2f); + Vector2 v = Graphics.screen(tile.drawx() - tile.block().size * tilesize / 2f + 0.25f, tile.drawy() + tile.block().size * tilesize / 2f); table.pack(); table.setPosition(v.x, v.y, Align.topRight); }); @@ -76,8 +96,7 @@ public class BlockConsumeFragment extends Fragment{ public void hide(){ table.clear(); - table.update(() -> { - }); + table.update(() -> {}); visible = false; } diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index dff94535ac..c64043c917 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -101,14 +101,6 @@ public class Tile implements PosTrait, TargetTrait{ return -1; } - public byte sizedRelativeTo(int cx, int cy){ - if(x == cx && y == cy - 1 - block().size / 2) return 1; - if(x == cx && y == cy + 1 + block().size / 2) return 3; - if(x == cx - 1 - block().size / 2 && y == cy) return 0; - if(x == cx + 1 + block().size / 2 && y == cy) return 2; - return -1; - } - public T entity(){ return (T) entity; } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java index 20bcb47b7a..9d93f621c3 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java @@ -14,6 +14,7 @@ import io.anuke.mindustry.graphics.Layer; import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Edges; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.BlockGroup; import io.anuke.ucore.core.Timers; @@ -265,7 +266,8 @@ public class ItemBridge extends Block{ Tile other = world.tile(entity.link); if(!linkValid(tile, other)){ - int i = tile.absoluteRelativeTo(to.x, to.y); + Tile edge = Edges.getFacingEdge(to, tile); + int i = tile.absoluteRelativeTo(edge.x, edge.y); IntSetIterator it = entity.incoming.iterator(); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java index d02f701ab1..f9d4a8aba2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -37,27 +37,62 @@ public class PowerGraph{ lastFrameUpdated = threads.getFrameID(); + boolean charge = false; + float totalInput = 0f; + float bufferInput = 0f; for(Tile producer : producers){ - totalInput += producer.entity.power.amount; + if (producer.block().consumesPower) { + bufferInput += producer.entity.power.amount; + } else { + totalInput += producer.entity.power.amount; + } } float maxOutput = 0f; + float bufferOutput = 0f; for(Tile consumer : consumers){ - maxOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + if (consumer.block().outputsPower) { + bufferOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + } else { + maxOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + } } - if (totalInput <= 0.0001f || maxOutput <= 0.0001f) { + if (maxOutput < totalInput) { + charge = true; + } + + if (totalInput + bufferInput <= 0.0001f || maxOutput + bufferOutput <= 0.0001f) { return; } - float inputUsed = Math.min(maxOutput / totalInput, 1f); + float bufferUsed = 0; + if (charge) { + bufferUsed = Math.min((totalInput - maxOutput) / bufferOutput, 1f); + } else { + bufferUsed = Math.min((maxOutput - totalInput) / bufferInput, 1f); + } + + float inputUsed = charge ? Math.min((maxOutput + bufferOutput) / totalInput, 1f) : 1f; for(Tile producer : producers){ + if (producer.block().consumesPower) { + if (!charge) { + producer.entity.power.amount -= producer.entity.power.amount * bufferUsed; + } + continue; + } producer.entity.power.amount -= producer.entity.power.amount * inputUsed; } - float outputSatisfied = Math.min(totalInput / maxOutput, 1f); + float outputSatisfied = charge ? 1f : Math.min((totalInput + bufferInput) / maxOutput, 1f); for(Tile consumer : consumers){ + if (consumer.block().outputsPower) { + if (charge) { + consumer.entity.power.amount += (consumer.block().powerCapacity - consumer.entity.power.amount) * bufferUsed; + } + continue; + } consumer.entity.power.amount += (consumer.block().powerCapacity - consumer.entity.power.amount) * outputSatisfied; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java b/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java index 65eacb6ef7..e2854906dd 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java @@ -42,6 +42,7 @@ public class Cultivator extends Drill{ stats.remove(BlockStat.drillTier); stats.add(BlockStat.drillTier, table -> { table.addImage("grass1").size(8 * 3).padBottom(3).padTop(3); + // TODO: find out localized name and add tool tip }); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java index 12a6635eb8..ac641ca4f7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java @@ -16,11 +16,14 @@ import io.anuke.mindustry.world.consumers.ConsumeLiquid; import io.anuke.mindustry.world.meta.BlockGroup; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Threads; @@ -133,7 +136,8 @@ public class Drill extends Block{ for(int i = 0; i < list.size; i++){ Item item = list.get(i); - table.addImage(item.name + "1").size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + Cell imageCell = table.addImage(item.name + "1").size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + StatValue.addToolTip(imageCell.getElement(), item); if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Pump.java b/core/src/io/anuke/mindustry/world/blocks/production/Pump.java index aaa483bb81..939587fa60 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Pump.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Pump.java @@ -64,15 +64,11 @@ public class Pump extends LiquidBlock{ if(isMultiblock()){ Liquid last = null; for(Tile other : tile.getLinkedTilesAs(this, drawTiles)){ - if(other == null) return false; - //can't place pump on block with multiple liquids - if(last != null && other.floor().liquidDrop != last){ + if(other.floor().liquidDrop == null) + continue; + if(other.floor().liquidDrop != last && last != null) return false; - } - - if(isValid(other)){ - last = other.floor().liquidDrop; - } + last = other.floor().liquidDrop; } return last != null; }else{ diff --git a/core/src/io/anuke/mindustry/world/blocks/units/CommandCenter.java b/core/src/io/anuke/mindustry/world/blocks/units/CommandCenter.java index 87e672e337..f8f540fdb9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/CommandCenter.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/CommandCenter.java @@ -81,11 +81,15 @@ public class CommandCenter extends Block{ public void buildTable(Tile tile, Table table){ CommandCenterEntity entity = tile.entity(); ButtonGroup group = new ButtonGroup<>(); + Table buttons = new Table(); for(UnitCommand cmd : UnitCommand.values()){ - table.addImageButton("command-" + cmd.name(), "toggle", 8*3, () -> threads.run(() -> Call.onCommandCenterSet(players[0], tile, cmd))).size(40f, 44f) + buttons.addImageButton("command-" + cmd.name(), "toggle", 8*3, () -> threads.run(() -> Call.onCommandCenterSet(players[0], tile, cmd))).size(40f, 44f) .checked(entity.command == cmd).group(group); } + table.add(buttons); + table.row(); + table.table("button", t -> t.label(() -> entity.command.localized()).center().growX()).growX().padTop(-5); } @Remote(called = Loc.server, forward = true, targets = Loc.both) diff --git a/core/src/io/anuke/mindustry/world/meta/StatValue.java b/core/src/io/anuke/mindustry/world/meta/StatValue.java index 5b95ff6e4d..ef3e51acef 100644 --- a/core/src/io/anuke/mindustry/world/meta/StatValue.java +++ b/core/src/io/anuke/mindustry/world/meta/StatValue.java @@ -1,5 +1,10 @@ package io.anuke.mindustry.world.meta; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.ucore.scene.Element; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.Tooltip; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; /** @@ -7,8 +12,38 @@ import io.anuke.ucore.scene.ui.layout.Table; */ public interface StatValue{ /** - * This method should all elements necessary to display this stat to the specified table. + * This method should provide all elements necessary to display this stat to the specified table. * For example, a stat that is just text would add label to the table. */ void display(Table table); + + /** + * This method adds an icon image together with a tool tip which contains the name of the item. + * @param table the table to add the image cell to. + * @param item The item which provides the tool tip content. + * @return the image cell which was created. The cell is not yet sized or padded. + */ + static Cell addImageWithToolTip(Table table, UnlockableContent item){ + + // Create a table cell with a new image as provided by the item + Cell imageCell = table.addImage(item.getContentIcon()); + + // Retrieve the image and add a tool tip with the item's name + addToolTip(imageCell.getElement(), item); + + // Return the table cell for further processing (sizing, padding, ...) + return imageCell; + } + + /** + * Adds a tool tip containing the item's localized name to the given element. + * @param element The element to assign the tool tip to. + * @param item The item which provides the tool tip content. + */ + static void addToolTip(Element element, UnlockableContent item){ + element.addListener(new Tooltip<>(new Table("clear"){{ + add(item.localizedName()); + margin(4); + }})); + } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java index 79348446ba..669f5baa2c 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java @@ -4,6 +4,8 @@ import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.function.Predicate; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import static io.anuke.mindustry.Vars.*; @@ -24,7 +26,12 @@ public class ItemFilterValue implements StatValue{ for(int i = 0; i < list.size; i++){ Item item = list.get(i); - table.addImage(item.region).size(8 * 3).padRight(2).padLeft(2); + + Cell imageCell = table.addImage(item.region); + imageCell.size(8 * 3).padRight(2).padLeft(2); + + StatValue.addToolTip(imageCell.getElement(), item); + if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java index 9a236306a3..7cb237296f 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java @@ -5,6 +5,9 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.ui.ItemImage; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; public class ItemListValue implements ContentStatValue{ @@ -38,11 +41,17 @@ public class ItemListValue implements ContentStatValue{ public void display(Table table){ if(items != null){ for(Item item : items){ - table.addImage(item.region).size(8 * 3).padRight(5); + Cell imageCell = table.addImage(item.region); + imageCell.size(8 * 3).padRight(5); + + StatValue.addToolTip(imageCell.getElement(), item); } }else{ for(ItemStack stack : stacks){ - table.add(new ItemImage(stack)).size(8 * 3).padRight(5); + ItemImage image = new ItemImage(stack); + table.add(image).size(8 * 3).padRight(5); + + StatValue.addToolTip(image, stack.item); } } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java index d42312c4d1..269c537dc5 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java @@ -5,6 +5,7 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.ui.ItemImage; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.scene.ui.layout.Table; public class ItemValue implements ContentStatValue{ @@ -22,6 +23,8 @@ public class ItemValue implements ContentStatValue{ @Override public void display(Table table){ //TODO better implementation, quantity support - table.add(new ItemImage(item)).size(8 * 3); + ItemImage image = new ItemImage(item); + table.add(image).size(8 * 3); + StatValue.addToolTip(image, item.item); } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java b/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java index 9a3417581f..9dc6a6bd0b 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java @@ -4,6 +4,9 @@ import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.function.Predicate; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.Tooltip; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import static io.anuke.mindustry.Vars.*; @@ -24,7 +27,10 @@ public class LiquidFilterValue implements StatValue{ for(int i = 0; i < list.size; i++){ Liquid item = list.get(i); - table.addImage(item.getContentIcon()).size(8 * 3).padRight(2).padLeft(2).padTop(2).padBottom(2); + + Cell imageCell = StatValue.addImageWithToolTip(table, item); + imageCell.size(8 * 3).padRight(2).padLeft(2).padTop(2).padBottom(2); + if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java b/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java index bdee87897e..d4d6734511 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java @@ -3,6 +3,9 @@ package io.anuke.mindustry.world.meta.values; import io.anuke.mindustry.game.UnlockableContent; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; public class LiquidValue implements ContentStatValue{ @@ -19,6 +22,7 @@ public class LiquidValue implements ContentStatValue{ @Override public void display(Table table){ - table.addImage(liquid.getContentIcon()).size(8 * 3); + Cell imageCell = StatValue.addImageWithToolTip(table, liquid); + imageCell.size(8 * 3); } } diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 1688653ecd..9ecc29d0d3 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.server; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.Timer; @@ -41,12 +42,18 @@ import static io.anuke.ucore.util.Log.*; public class ServerControl extends Module{ private static final int roundExtraTime = 12; + //in bytes: 512 kb is max + private static final int maxLogLength = 1024 * 512; private final CommandHandler handler = new CommandHandler(""); + private final FileHandle logFolder = Gdx.files.local("logs/"); + + private FileHandle currentLogFile; private int gameOvers; private boolean inExtraRound; private Task lastTask; + public ServerControl(String[] args){ Settings.defaultList( "shufflemode", "normal", @@ -56,7 +63,8 @@ public class ServerControl extends Module{ "sector_y", 1, "shuffle", true, "crashreport", false, - "port", port + "port", port, + "logging", true ); Log.setLogger(new LogHandler(){ @@ -64,22 +72,27 @@ public class ServerControl extends Module{ @Override public void info(String text, Object... args){ - print("&lg&fb" + "[INFO] " + format(text, args)); + print("&lg&fb" + "[INFO] " + text, args); } @Override public void err(String text, Object... args){ - print("&lr&fb" + "[ERR!] " + format(text, args)); + print("&lr&fb" + "[ERR!] " + text, args); } @Override public void warn(String text, Object... args){ - print("&ly&fb" + "[WARN] " + format(text, args)); + print("&ly&fb" + "[WARN] " + text, args); } @Override public void print(String text, Object... args){ - System.out.println("[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", args)); + String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", args); + System.out.println(result); + + if(Settings.getBool("logging")){ + logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", false, args)); + } } }); @@ -311,7 +324,7 @@ public class ServerControl extends Module{ return; } - Call.sendMessage("[GRAY][[Server]:[] " + arg[0]); + Call.sendMessage("[scarlet][[Server]:[] " + arg[0]); info("&lyServer: &lb{0}", arg[0]); }); @@ -357,6 +370,13 @@ public class ServerControl extends Module{ info("Crash reporting is now {0}.", value ? "on" : "off"); }); + handler.register("logging", "", "Disables or enables server logs", arg -> { + boolean value = arg[0].equalsIgnoreCase("on"); + Settings.putBool("logging", value); + Settings.save(); + info("Logging is now {0}.", value ? "on" : "off"); + }); + handler.register("strict", "", "Disables or enables strict mode", arg -> { boolean value = arg[0].equalsIgnoreCase("on"); netServer.admins.setStrict(value); @@ -400,6 +420,7 @@ public class ServerControl extends Module{ Player target = playerGroup.find(p -> p.name.equals(arg[0])); if(target != null){ + Call.sendMessage("[scarlet] " + target.name + " has been kicked by the server."); netServer.kick(target.con.id, KickReason.kick); info("It is done."); }else{ @@ -425,6 +446,13 @@ public class ServerControl extends Module{ }else{ err("Invalid type."); } + + for(Player player : playerGroup.all()){ + if(netServer.admins.isIDBanned(player.uuid)){ + Call.sendMessage("[scarlet] " + player.name + " has been banned."); + netServer.kick(player.con.id, KickReason.banned); + } + } }); handler.register("bans", "List all banned IPs and IDs.", arg -> { @@ -724,4 +752,23 @@ public class ServerControl extends Module{ state.set(State.menu); } } + + private void logToFile(String text){ + if(currentLogFile != null && currentLogFile.length() > maxLogLength){ + String date = DateTimeFormatter.ofPattern("MM-dd-yyyy | HH:mm:ss").format(LocalDateTime.now()); + currentLogFile.writeString("[End of log file. Date: "+ date + "]\n", true); + currentLogFile = null; + } + + if(currentLogFile == null){ + int i = 0; + while(logFolder.child("log-" + i + ".txt").length() >= maxLogLength){ + i ++; + } + + currentLogFile = logFolder.child("log-" + i + ".txt"); + } + + currentLogFile.writeString(text + "\n", true); + } }