PvP merge

This commit is contained in:
Anuken
2018-10-14 10:01:50 -04:00
16 changed files with 127 additions and 49 deletions

View File

@@ -27,7 +27,7 @@ allprojects {
appName = 'Mindustry' appName = 'Mindustry'
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
roboVMVersion = '2.3.0' roboVMVersion = '2.3.0'
uCoreVersion = '628ced32dbceefe9096c6acc9639cd39b1a867f4' uCoreVersion = 'abd096135c0c3da6f42b781851118b8725b1c676'
getVersionString = { getVersionString = {
String buildVersion = getBuildVersion() String buildVersion = getBuildVersion()

View File

@@ -11,7 +11,8 @@ text.link.wiki.description=official Mindustry wiki
text.linkfail=Failed to open link!\nThe URL has been copied to your cliboard. text.linkfail=Failed to open link!\nThe URL has been copied to your cliboard.
text.editor.web=The web version does not support the editor!\nDownload the game to use it. text.editor.web=The web version does not support the editor!\nDownload the game to use it.
text.web.unsupported=The web version does not support this feature! Download the game to use it. text.web.unsupported=The web version does not support this feature! Download the game to use it.
text.gameover=Your core has been destroyed text.gameover=Game Over
text.gameover.pvp=The[accent] {0}[] team is victorious!
text.sector.gameover=This sector has been lost. Re-deploy? text.sector.gameover=This sector has been lost. Re-deploy?
text.sector.retry=Retry text.sector.retry=Retry
text.highscore=[accent]New highscore! text.highscore=[accent]New highscore!
@@ -209,6 +210,7 @@ text.saving=[accent]Saving...
text.wave=[orange]Wave {0} text.wave=[orange]Wave {0}
text.wave.waiting=[LIGHT_GRAY]Wave in {0} text.wave.waiting=[LIGHT_GRAY]Wave in {0}
text.waiting=[LIGHT_GRAY]Waiting... text.waiting=[LIGHT_GRAY]Waiting...
text.waiting.players=Waiting for players...
text.wave.enemies=[LIGHT_GRAY]{0} Enemies Remaining text.wave.enemies=[LIGHT_GRAY]{0} Enemies Remaining
text.wave.enemy=[LIGHT_GRAY]{0} Enemy Remaining text.wave.enemy=[LIGHT_GRAY]{0} Enemy Remaining
text.loadimage=Load Image text.loadimage=Load Image
@@ -218,7 +220,8 @@ text.custom=Custom
text.builtin=Built-In text.builtin=Built-In
text.map.delete.confirm=Are you sure you want to delete this map? This action cannot be undone! text.map.delete.confirm=Are you sure you want to delete this map? This action cannot be undone!
text.map.random=[accent]Random Map text.map.random=[accent]Random Map
text.map.nospawn=This map does not have any cores for the player to spawn in! Add a [ROYAL]blue[] core to this map in the editor. text.map.nospawn=This map does not have any cores for the player to spawn in! Add a[ROYAL] blue[] core to this map in the editor.
text.map.nospawn.pvp=This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor.
text.map.invalid=Error loading map: corrupted or invalid map file. text.map.invalid=Error loading map: corrupted or invalid map file.
text.editor.brush=Brush text.editor.brush=Brush
text.editor.slope=\\ text.editor.slope=\\
@@ -425,6 +428,8 @@ mode.sandbox.description=infinite resources and no timer for waves.
mode.custom.warning=Note that blocks unlocked in custom games are not carried over to sectors.\n\n[LIGHT_GRAY]In sandbox, only blocks unlocked with sector play can be used. mode.custom.warning=Note that blocks unlocked in custom games are not carried over to sectors.\n\n[LIGHT_GRAY]In sandbox, only blocks unlocked with sector play can be used.
mode.freebuild.name=freebuild mode.freebuild.name=freebuild
mode.freebuild.description=limited resources and no timer for waves. mode.freebuild.description=limited resources and no timer for waves.
mode.pvp.name=PvP
mode.pvp.description=fight against other players locally.
content.item.name=Items content.item.name=Items
content.liquid.name=Liquids content.liquid.name=Liquids
@@ -651,6 +656,13 @@ block.rtg-generator.name=RTG Generator
block.spectre.name=Spectre block.spectre.name=Spectre
block.meltdown.name=Meltdown block.meltdown.name=Meltdown
team.blue.name=blue
team.red.name=red
team.orange.name=orange
team.none.name=gray
team.green.name=green
team.purple.name=purple
unit.alpha-drone.name=Alpha Drone unit.alpha-drone.name=Alpha Drone
unit.spirit.name=Spirit Drone unit.spirit.name=Spirit Drone
unit.spirit.description=The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks. unit.spirit.description=The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks.

View File

@@ -13,6 +13,7 @@ import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Saves; import io.anuke.mindustry.game.Saves;
import io.anuke.mindustry.game.Unlocks; import io.anuke.mindustry.game.Unlocks;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.input.DefaultKeybinds; import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.input.DesktopInput; import io.anuke.mindustry.input.DesktopInput;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
@@ -27,6 +28,10 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityQuery; import io.anuke.ucore.entities.EntityQuery;
import io.anuke.ucore.modules.Module; import io.anuke.ucore.modules.Module;
import io.anuke.ucore.util.Atlas; import io.anuke.ucore.util.Atlas;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Strings;
import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -153,11 +158,24 @@ public class Control extends Module{
threads.runGraphics(() -> { threads.runGraphics(() -> {
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
ui.restart.show(); //the restart dialog can show info for any number of scenarios
state.set(State.menu); Call.onGameOver(event.winner);
}); });
}); });
//autohost for pvp sectors
Events.on(WorldLoadEvent.class, event -> {
if(state.mode.isPvp && !Net.active()){
try{
Net.host(port);
players[0].isAdmin = true;
}catch(IOException e){
ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false)));
threads.runDelay(() -> state.set(State.menu));
}
}
});
Events.on(WorldLoadEvent.class, event -> threads.runGraphics(() -> Events.fire(new WorldLoadGraphicsEvent()))); Events.on(WorldLoadEvent.class, event -> threads.runGraphics(() -> Events.fire(new WorldLoadGraphicsEvent())));
Events.on(TileChangeEvent.class, event -> { Events.on(TileChangeEvent.class, event -> {

View File

@@ -6,6 +6,7 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Teams; import io.anuke.mindustry.game.Teams;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.type.ItemStack;
@@ -77,11 +78,26 @@ public class Logic extends Module{
Events.fire(new WaveEvent()); Events.fire(new WaveEvent());
} }
//this never triggers in PvP; only for checking sector game-overs
private void checkGameOver(){ private void checkGameOver(){
if(!state.mode.isPvp && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){ if(!state.mode.isPvp && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){
state.gameOver = true; state.gameOver = true;
Events.fire(new GameOverEvent()); Events.fire(new GameOverEvent(waveTeam));
}else if(state.mode.isPvp){
Team alive = null;
for(Team team : Team.all){
if(state.teams.get(team).cores.size > 0){
if(alive != null){
return;
}
alive = team;
}
}
if(alive != null && !state.gameOver){
state.gameOver = true;
Events.fire(new GameOverEvent(alive));
}
} }
} }

View File

@@ -202,6 +202,7 @@ public class NetServer extends Module{
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
}); });
player.setTeam(min); player.setTeam(min);
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
} }
connections.put(id, player); connections.put(id, player);
@@ -414,6 +415,15 @@ public class NetServer extends Module{
Log.info("&y{0} has connected.", player.name); Log.info("&y{0} has connected.", player.name);
} }
@Remote(called = Loc.both)
public static void onGameOver(Team winner){
threads.runGraphics(() -> ui.restart.show(winner));
}
public boolean isWaitingForPlayers(){
return state.mode.isPvp && playerGroup.size() < 2;
}
public void update(){ public void update(){
if(threads.isEnabled() && !threads.isOnThread()) return; if(threads.isEnabled() && !threads.isOnThread()) return;

View File

@@ -336,11 +336,13 @@ public class Renderer extends RendererModule{
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
float lastX = camera.position.x, lastY = camera.position.y;
super.resize(width, height); super.resize(width, height);
for(Player player : players){ for(Player player : players){
control.input(player.playerIndex).resetCursor(); control.input(player.playerIndex).resetCursor();
} }
camera.position.set(players[0].x, players[0].y, 0); camera.update();
camera.position.set(lastX, lastY, 0f);
} }
@Override @Override

View File

@@ -257,13 +257,27 @@ public class World extends Module{
endMapLoad(); endMapLoad();
if(!headless && state.teams.get(players[0].getTeam()).cores.size == 0){ if(!headless){
ui.showError("$text.map.nospawn"); if(state.teams.get(players[0].getTeam()).cores.size == 0){
threads.runDelay(() -> state.set(State.menu)); ui.showError("$text.map.nospawn");
invalidMap = true; invalidMap = true;
}else if(state.mode.isPvp){
invalidMap = true;
for(Team team : Team.all){
if(state.teams.get(team).cores.size != 0 && team != players[0].getTeam()){
invalidMap = false;
}
}
if(invalidMap){
ui.showError("$text.map.nospawn.pvp");
}
}
}else{ }else{
invalidMap = false; invalidMap = false;
} }
if(invalidMap) threads.runDelay(() -> state.set(State.menu));
} }
public void notifyChanged(Tile tile){ public void notifyChanged(Tile tile){

View File

@@ -8,7 +8,6 @@ import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Queue; import com.badlogic.gdx.utils.Queue;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote; import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Mechs; import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.content.fx.UnitFx; import io.anuke.mindustry.content.fx.UnitFx;
import io.anuke.mindustry.entities.effect.ScorchDecal; import io.anuke.mindustry.entities.effect.ScorchDecal;
@@ -727,12 +726,14 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
//region utility methods //region utility methods
public void toggleTeam(){
team = (team == Team.blue ? Team.red : Team.blue);
}
/** Resets all values of the player.*/ /** Resets all values of the player.*/
public void reset(){ public void reset(){
resetNoAdd();
add();
}
public void resetNoAdd(){
status.clear(); status.clear();
team = Team.blue; team = Team.blue;
inventory.clear(); inventory.clear();
@@ -744,8 +745,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
boostHeat = drownTime = hitTime = 0f; boostHeat = drownTime = hitTime = 0f;
mech = (isMobile ? Mechs.starterMobile : Mechs.starterDesktop); mech = (isMobile ? Mechs.starterMobile : Mechs.starterDesktop);
placeQueue.clear(); placeQueue.clear();
add();
} }
public boolean isShooting(){ public boolean isShooting(){

View File

@@ -27,7 +27,11 @@ public class EventType{
} }
public static class GameOverEvent implements Event{ public static class GameOverEvent implements Event{
public final Team winner;
public GameOverEvent(Team winner){
this.winner = winner;
}
} }
/** /**

View File

@@ -25,7 +25,6 @@ public enum GameMode{
pvp{{ pvp{{
disableWaves = true; disableWaves = true;
isPvp = true; isPvp = true;
hidden = true;
enemyCoreBuildRadius = 600f; enemyCoreBuildRadius = 600f;
respawnTime = 60 * 10; respawnTime = 60 * 10;
}}; }};

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.game; package io.anuke.mindustry.game;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import io.anuke.ucore.util.Bundles;
public enum Team{ public enum Team{
none(Color.DARK_GRAY), none(Color.DARK_GRAY),
@@ -18,4 +19,8 @@ public enum Team{
this.color = color; this.color = color;
intColor = Color.rgba8888(color); intColor = Color.rgba8888(color);
} }
public String localized(){
return Bundles.get("team." + name() + ".name");
}
} }

View File

@@ -183,6 +183,7 @@ public class NetworkIO{
Entities.clear(); Entities.clear();
int id = stream.readInt(); int id = stream.readInt();
player.resetNoAdd();
player.read(stream, TimeUtils.millis()); player.read(stream, TimeUtils.millis());
player.resetID(id); player.resetID(id);
player.add(); player.add();
@@ -258,7 +259,6 @@ public class NetworkIO{
i += consecutives; i += consecutives;
} }
player.reset();
state.teams = new Teams(); state.teams = new Teams();
byte teams = stream.readByte(); byte teams = stream.readByte();

View File

@@ -1,12 +1,14 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.maps.Sector; import io.anuke.mindustry.maps.Sector;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class RestartDialog extends FloatingDialog{ public class RestartDialog extends FloatingDialog{
private Team winner;
public RestartDialog(){ public RestartDialog(){
super("$text.gameover"); super("$text.gameover");
@@ -14,13 +16,25 @@ public class RestartDialog extends FloatingDialog{
shown(this::rebuild); shown(this::rebuild);
} }
public void show(Team winner){
this.winner = winner;
show();
}
void rebuild(){ void rebuild(){
buttons().clear(); buttons().clear();
content().clear(); content().clear();
buttons().margin(10); buttons().margin(10);
if(world.getSector() == null){ if(state.mode.isPvp){
content().add(Bundles.format("text.gameover.pvp",winner.localized())).pad(6);
buttons().addButton("$text.menu", () -> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}else if(world.getSector() == null){
if(control.isHighScore()){ if(control.isHighScore()){
content().add("$text.highscore").pad(6); content().add("$text.highscore").pad(6);
content().row(); content().row();

View File

@@ -145,6 +145,11 @@ public class HudFragment extends Fragment{
t.table("clear", top -> top.add("[orange]< " + Bundles.get("text.paused") + " >").pad(6).get().setFontScale(fontScale * 1.5f)); t.table("clear", top -> top.add("[orange]< " + Bundles.get("text.paused") + " >").pad(6).get().setFontScale(fontScale * 1.5f));
}); });
parent.fill(t -> {
t.visible(() -> netServer.isWaitingForPlayers() && !state.is(State.menu));
t.table("clear", c -> c.margin(10).add("$text.waiting.players"));
});
//'core is under attack' table //'core is under attack' table
parent.fill(t -> { parent.fill(t -> {
float notifDuration = 240f; float notifDuration = 240f;

View File

@@ -36,6 +36,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import static io.anuke.mindustry.Vars.netServer;
import static io.anuke.mindustry.Vars.state; import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.unitGroups; import static io.anuke.mindustry.Vars.unitGroups;
@@ -204,7 +205,7 @@ public class CoreBlock extends StorageBlock{
if(entity.progress >= 1f){ if(entity.progress >= 1f){
Call.onUnitRespawn(tile, entity.currentUnit); Call.onUnitRespawn(tile, entity.currentUnit);
} }
}else{ }else if(!netServer.isWaitingForPlayers()){
entity.warmup += Timers.delta(); entity.warmup += Timers.delta();
if(entity.solid && entity.warmup > 60f && unitGroups[tile.getTeamID()].getByID(entity.droneID) == null && !Net.client()){ if(entity.solid && entity.warmup > 60f && unitGroups[tile.getTeamID()].getByID(entity.droneID) == null && !Net.client()){
@@ -241,7 +242,6 @@ public class CoreBlock extends StorageBlock{
public class CoreEntity extends TileEntity implements SpawnerTrait{ public class CoreEntity extends TileEntity implements SpawnerTrait{
public Unit currentUnit; public Unit currentUnit;
public float shieldHeat;
int droneID = -1; int droneID = -1;
boolean solid = true; boolean solid = true;
float warmup; float warmup;
@@ -251,7 +251,7 @@ public class CoreBlock extends StorageBlock{
@Override @Override
public void updateSpawning(Unit unit){ public void updateSpawning(Unit unit){
if(currentUnit == null){ if(!netServer.isWaitingForPlayers() && currentUnit == null){
currentUnit = unit; currentUnit = unit;
progress = 0f; progress = 0f;
unit.set(tile.drawx(), tile.drawy()); unit.set(tile.drawx(), tile.drawy());

View File

@@ -43,7 +43,6 @@ public class ServerControl extends Module{
private final CommandHandler handler = new CommandHandler(""); private final CommandHandler handler = new CommandHandler("");
private int gameOvers; private int gameOvers;
private boolean inExtraRound; private boolean inExtraRound;
private Team winnerTeam;
private Task lastTask; private Task lastTask;
public ServerControl(String[] args){ public ServerControl(String[] args){
@@ -121,8 +120,8 @@ public class ServerControl extends Module{
while(map == previous) map = maps.random(); while(map == previous) map = maps.random();
} }
Call.onInfoMessage((state.mode.isPvp && winnerTeam != null Call.onInfoMessage((state.mode.isPvp
? "[YELLOW]The " + winnerTeam.name() + " team is victorious![]" : "[SCARLET]Game over![]") ? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]")
+ "\nNext selected map:[accent] "+map.name+"[]" + "\nNext selected map:[accent] "+map.name+"[]"
+ (map.meta.author() != null ? " by[accent] " + map.meta.author() + "[]" : "") + "."+ + (map.meta.author() != null ? " by[accent] " + map.meta.author() + "[]" : "") + "."+
"\nNew game begins in " + roundExtraTime + " seconds."); "\nNew game begins in " + roundExtraTime + " seconds.");
@@ -650,7 +649,7 @@ public class ServerControl extends Module{
info("&lyCore destroyed."); info("&lyCore destroyed.");
inExtraRound = false; inExtraRound = false;
Events.fire(new GameOverEvent()); Events.fire(new GameOverEvent(Team.red));
}); });
handler.register("traceblock", "<x> <y>", "Prints debug info about a block", arg -> { handler.register("traceblock", "<x> <y>", "Prints debug info about a block", arg -> {
@@ -882,29 +881,10 @@ public class ServerControl extends Module{
} }
} }
private void checkPvPGameOver(){
Team alive = null;
for(Team team : Team.all){
if(state.teams.get(team).cores.size > 0){
if(alive != null){
return;
}
alive = team;
}
}
if(alive != null && !state.gameOver){
state.gameOver = true;
winnerTeam = alive;
Events.fire(new GameOverEvent());
}
}
@Override @Override
public void update(){ public void update(){
if(!inExtraRound && state.mode.isPvp){ if(!inExtraRound && state.mode.isPvp){
checkPvPGameOver(); // checkPvPGameOver();
} }
} }
} }