Merge branch 'androidmultiaiplayers' into 'master'

3/4 player matches on Android

See merge request core-developers/forge!295
This commit is contained in:
Michael Kamensky
2018-03-21 18:20:22 +00:00
9 changed files with 154 additions and 89 deletions

View File

@@ -815,6 +815,10 @@ public class FDeckChooser extends FScreen {
/*if(selectedDeckType.equals(DeckType.STANDARD_CARDGEN_DECK)){
return DeckgenUtil.buildCardGenDeck(lstDecks.getSelectedItem().getName());
}*/
//ensure a deck is selected first
if(lstDecks.getSelectedIndex() == -1){
lstDecks.setSelectedIndex(0);
}
DeckProxy proxy = lstDecks.getSelectedItem();
if (proxy == null) { return null; }
return proxy.getDeck();

View File

@@ -43,7 +43,7 @@ import forge.util.Utils;
public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
private static final ForgePreferences prefs = FModel.getPreferences();
private static final float PADDING = Utils.scale(5);
public static final int MAX_PLAYERS = 2; //8; //TODO: Support multiplayer
public static final int MAX_PLAYERS = 4;
private static final FSkinFont VARIANTS_FONT = FSkinFont.get(12);
// General variables
@@ -84,8 +84,6 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
public LobbyScreen(String headerCaption, FPopupMenu menu, GameLobby lobby0) {
super(headerCaption, menu);
initLobby(lobby0);
btnStart.setEnabled(false); //disable start button until decks loaded
add(lblPlayers);
@@ -99,13 +97,23 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
@Override
public void handleEvent(FEvent e) {
int numPlayers = getNumPlayers();
while(lobby.getNumberOfSlots() < getNumPlayers()){
lobby.addSlot();
}
while(lobby.getNumberOfSlots() > getNumPlayers()){
lobby.removeSlot(lobby.getNumberOfSlots()-1);
}
for (int i = 0; i < MAX_PLAYERS; i++) {
playerPanels.get(i).setVisible(i < numPlayers);
if(i<playerPanels.size()) {
playerPanels.get(i).setVisible(i < numPlayers);
}
}
playersScroll.revalidate();
}
});
initLobby(lobby0);
add(lblVariants);
add(cbVariants);
cbVariants.setFont(VARIANTS_FONT);
@@ -148,12 +156,16 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
public void run() {
playerPanels.get(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, FPref.COMMANDER_P1_DECK_STATE, FPref.TINY_LEADER_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK);
playerPanels.get(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, FPref.COMMANDER_P2_DECK_STATE, FPref.TINY_LEADER_P2_DECK_STATE, DeckType.COLOR_DECK);
/*playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK);
if(getNumPlayers()>2) {
playerPanels.get(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, DeckType.COLOR_DECK);
}
if(getNumPlayers()>3) {
playerPanels.get(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, DeckType.COLOR_DECK);
}
/*playerPanels.get(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(5).initialize(FPref.CONSTRUCTED_P6_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(6).initialize(FPref.CONSTRUCTED_P7_DECK_STATE, DeckType.COLOR_DECK);
playerPanels.get(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK);*/ //TODO: Support multiplayer and improve performance of loading this screen by using background thread
playerPanels.get(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK);*/ //TODO: Improve performance of loading this screen by using background thread
FThreads.invokeInEdtLater(new Runnable() {
@Override
@@ -164,9 +176,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
}
});
//disable player count for now until multiplayer supported
lblPlayers.setEnabled(false);
cbPlayerCount.setEnabled(false);
lblPlayers.setEnabled(true);
cbPlayerCount.setEnabled(true);
}
public GameLobby getLobby() {
@@ -180,6 +191,9 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
btnStart.setEnabled(hasControl);
lblVariants.setEnabled(hasControl);
cbVariants.setEnabled(hasControl);
while(lobby.getNumberOfSlots()<getNumPlayers()){
lobby.addSlot();
}
}
private void updateVariantSelection() {
@@ -209,7 +223,7 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
}
void updateLayoutForVariants() {
for (int i = 0; i < MAX_PLAYERS; i++) {
for (int i = 0; i < cbPlayerCount.getSelectedItem(); i++) {
playerPanels.get(i).updateVariantControlsVisibility();
}
playersScroll.revalidate();
@@ -447,12 +461,11 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
@Override
public void update(final boolean fullUpdate) {
int playerCount = lobby.getNumberOfSlots();
cbPlayerCount.setSelectedItem(playerCount);
updateVariantSelection();
final boolean allowNetworking = lobby.isAllowNetworking();
for (int i = 0; i < MAX_PLAYERS; i++) {
for (int i = 0; i < cbPlayerCount.getSelectedItem(); i++) {
final boolean hasPanel = i < playerPanels.size();
if (i < playerCount) {
// visible panels
@@ -465,6 +478,12 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView {
}
else {
panel = new PlayerPanel(this, allowNetworking, i, slot, lobby.mayEdit(i), lobby.hasControl());
if(i==2) {
panel.initialize(FPref.CONSTRUCTED_P3_DECK_STATE, FPref.COMMANDER_P3_DECK_STATE, FPref.TINY_LEADER_P3_DECK_STATE, DeckType.COLOR_DECK);
}
if(i==3) {
panel.initialize(FPref.CONSTRUCTED_P4_DECK_STATE, FPref.COMMANDER_P4_DECK_STATE, FPref.TINY_LEADER_P4_DECK_STATE, DeckType.COLOR_DECK);
}
playerPanels.add(panel);
playersScroll.add(panel);
isNewPanel = true;

View File

@@ -232,7 +232,7 @@ public class PlayerPanel extends FContainer {
setMayControl(mayControl0);
//disable team combo boxes for now
cbTeam.setEnabled(false);
cbTeam.setEnabled(true);
}
public void initialize(FPref savedStateSetting, FPref savedStateSettingCommander, FPref savedStateSettingTinyLeader, DeckType defaultDeckType) {

View File

@@ -119,7 +119,7 @@ public class MatchController extends AbstractGuiGame {
final List<VPlayerPanel> playerPanels = new ArrayList<VPlayerPanel>();
for (final PlayerView p : allPlayers) {
final boolean isLocal = isLocalPlayer(p);
final VPlayerPanel playerPanel = new VPlayerPanel(p, isLocal || noHumans);
final VPlayerPanel playerPanel = new VPlayerPanel(p, isLocal || noHumans, allPlayers.size());
if (isLocal && !playerPanels.isEmpty()) {
playerPanels.add(0, playerPanel); //ensure local player always first among player panels
}
@@ -359,19 +359,21 @@ public class MatchController extends AbstractGuiGame {
private static void actuateMatchPreferences() {
final ForgePreferences prefs = FModel.getPreferences();
final VPhaseIndicator fvAi = view.getTopPlayerPanel().getPhaseIndicator();
fvAi.getLabel(PhaseType.UPKEEP).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_UPKEEP));
fvAi.getLabel(PhaseType.DRAW).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DRAW));
fvAi.getLabel(PhaseType.MAIN1).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_MAIN1));
fvAi.getLabel(PhaseType.COMBAT_BEGIN).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_BEGINCOMBAT));
fvAi.getLabel(PhaseType.COMBAT_DECLARE_ATTACKERS).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DECLAREATTACKERS));
fvAi.getLabel(PhaseType.COMBAT_DECLARE_BLOCKERS).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DECLAREBLOCKERS));
fvAi.getLabel(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_FIRSTSTRIKE));
fvAi.getLabel(PhaseType.COMBAT_DAMAGE).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_COMBATDAMAGE));
fvAi.getLabel(PhaseType.COMBAT_END).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_ENDCOMBAT));
fvAi.getLabel(PhaseType.MAIN2).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_MAIN2));
fvAi.getLabel(PhaseType.END_OF_TURN).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_EOT));
fvAi.getLabel(PhaseType.CLEANUP).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_CLEANUP));
for (int i=0; i<view.getPlayerPanelsList().size()-1; ++i){
VPhaseIndicator fvAi = view.getPlayerPanelsList().get(i).getPhaseIndicator();
fvAi.getLabel(PhaseType.UPKEEP).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_UPKEEP));
fvAi.getLabel(PhaseType.DRAW).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DRAW));
fvAi.getLabel(PhaseType.MAIN1).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_MAIN1));
fvAi.getLabel(PhaseType.COMBAT_BEGIN).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_BEGINCOMBAT));
fvAi.getLabel(PhaseType.COMBAT_DECLARE_ATTACKERS).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DECLAREATTACKERS));
fvAi.getLabel(PhaseType.COMBAT_DECLARE_BLOCKERS).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_DECLAREBLOCKERS));
fvAi.getLabel(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_FIRSTSTRIKE));
fvAi.getLabel(PhaseType.COMBAT_DAMAGE).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_COMBATDAMAGE));
fvAi.getLabel(PhaseType.COMBAT_END).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_ENDCOMBAT));
fvAi.getLabel(PhaseType.MAIN2).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_MAIN2));
fvAi.getLabel(PhaseType.END_OF_TURN).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_EOT));
fvAi.getLabel(PhaseType.CLEANUP).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_AI_CLEANUP));
}
final VPhaseIndicator fvHuman = view.getBottomPlayerPanel().getPhaseIndicator();
fvHuman.getLabel(PhaseType.UPKEEP).setStopAtPhase(prefs.getPrefBoolean(FPref.PHASE_HUMAN_UPKEEP));

View File

@@ -1,10 +1,6 @@
package forge.screens.match;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.tuple.Pair;
@@ -63,6 +59,7 @@ public class MatchScreen extends FScreen {
public static FSkinColor BORDER_COLOR = FSkinColor.get(Colors.CLR_BORDERS);
private final Map<PlayerView, VPlayerPanel> playerPanels = Maps.newHashMap();
private List<VPlayerPanel> playerPanelsList;
private final VGameMenu gameMenu;
private final VPlayers players;
private final VLog log;
@@ -80,10 +77,16 @@ public class MatchScreen extends FScreen {
for (VPlayerPanel playerPanel : playerPanels0) {
playerPanels.put(playerPanel.getPlayer(), scroller.add(playerPanel));
playerPanel.setFlipped(true);
}
bottomPlayerPanel = playerPanels0.get(0);
bottomPlayerPanel.setFlipped(false);
topPlayerPanel = playerPanels0.get(1);
topPlayerPanel.setFlipped(true);
playerPanelsList=playerPanels0;
//reorder list so bottom player is at the end of the list ensuring top to bottom turn order
playerPanelsList.remove(bottomPlayerPanel);
playerPanelsList.add(bottomPlayerPanel);
bottomPlayerPrompt = add(new VPrompt("", "",
new FEventHandler() {
@@ -154,6 +157,14 @@ public class MatchScreen extends FScreen {
}
}
private boolean is4Player(){
return playerPanels.keySet().size()==4;
}
private boolean is3Player(){
return playerPanels.keySet().size()==3;
}
private IGameController getGameController() {
return MatchController.instance.getGameController();
}
@@ -274,6 +285,10 @@ public class MatchScreen extends FScreen {
return playerPanels;
}
public List<VPlayerPanel> getPlayerPanelsList() {
return playerPanelsList;
}
@Override
public void onClose(Callback<Boolean> canCloseCallback) {
MatchController.writeMatchPreferences();
@@ -492,64 +507,64 @@ public class MatchScreen extends FScreen {
@Override
public void drawOverlay(Graphics g) {
float midField = topPlayerPanel.getBottom();
float midField;
float x = 0;
float y = midField - topPlayerPanel.getField().getHeight();
float y;
float w = getWidth();
//field separator lines
if (!Forge.isLandscapeMode()) {
if (topPlayerPanel.getSelectedTab() == null) {
y++; //ensure border goes all the way across under avatar
for (VPlayerPanel playerPanel: playerPanelsList){
midField = playerPanel.getTop();
y = midField - playerPanel.getField().getHeight();
if(playerPanel.getSelectedTab() == null) {
y++;
}
g.drawLine(1, BORDER_COLOR, x, y, w, y);
}
g.drawLine(1, BORDER_COLOR, 0, y, w, y);
}
y = midField - 0.5f;
g.drawLine(1, BORDER_COLOR, x, y, w, y);
for (VPlayerPanel playerPanel: playerPanelsList){
midField = playerPanel.getTop();
y = midField - 0.5f;
g.drawLine(1, BORDER_COLOR, x, y, w, y);
}
if (!Forge.isLandscapeMode()) {
y = midField + bottomPlayerPanel.getField().getHeight();
g.drawLine(1, BORDER_COLOR, 0, y, w, y);
y = bottomPlayerPanel.getTop() + bottomPlayerPanel.getField().getHeight();
g.drawLine(1, BORDER_COLOR, x, y, w, y);
}
}
@Override
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
float totalHeight = visibleHeight + extraHeight;
float avatarHeight = VAvatar.HEIGHT;
if(is4Player() || is3Player()){
avatarHeight *= 0.5f;
}
float playerCount = getPlayerPanels().keySet().size();
//determine player panel heights based on visibility of zone displays
float topPlayerPanelHeight, bottomPlayerPanelHeight;
if (Forge.isLandscapeMode()) {
topPlayerPanelHeight = totalHeight / 2;
bottomPlayerPanelHeight = topPlayerPanelHeight;
float cardRowsHeight = totalHeight - playerCount * avatarHeight;
float totalCardRows=0;
for(VPlayerPanel playerPanel:playerPanelsList){
if(playerPanel.getSelectedTab() != null){
totalCardRows += 1;
}
totalCardRows += 2;
}
else {
float cardRowsHeight = totalHeight - 2 * VAvatar.HEIGHT;
if (topPlayerPanel.getSelectedTab() == null) {
if (bottomPlayerPanel.getSelectedTab() != null) {
topPlayerPanelHeight = cardRowsHeight * 2f / 5f;
bottomPlayerPanelHeight = cardRowsHeight * 3f / 5f;
}
else {
topPlayerPanelHeight = cardRowsHeight / 2f;
bottomPlayerPanelHeight = topPlayerPanelHeight;
}
float y=0;
for(VPlayerPanel playerPanel:playerPanelsList){
float panelHeight;
if(playerPanel.getSelectedTab() != null){
panelHeight = cardRowsHeight * 3f / totalCardRows;
}else{
panelHeight = cardRowsHeight * 2f / totalCardRows;
}
else if (bottomPlayerPanel.getSelectedTab() == null) {
topPlayerPanelHeight = cardRowsHeight * 3f / 5f;
bottomPlayerPanelHeight = cardRowsHeight * 2f / 5f;
}
else {
topPlayerPanelHeight = cardRowsHeight / 2f;
bottomPlayerPanelHeight = topPlayerPanelHeight;
}
topPlayerPanelHeight += VAvatar.HEIGHT;
bottomPlayerPanelHeight += VAvatar.HEIGHT;
panelHeight += avatarHeight;
playerPanel.setBounds(0, y, visibleWidth, panelHeight);
y += panelHeight;
}
topPlayerPanel.setBounds(0, 0, visibleWidth, topPlayerPanelHeight);
bottomPlayerPanel.setBounds(0, totalHeight - bottomPlayerPanelHeight, visibleWidth, bottomPlayerPanelHeight);
return new ScrollBounds(visibleWidth, totalHeight);
}

View File

@@ -27,6 +27,12 @@ public class VAvatar extends FDisplayObject {
setSize(WIDTH, HEIGHT);
}
public VAvatar(PlayerView player0, float size) {
player = player0;
image = MatchController.getPlayerAvatar(player);
setSize(size, size);
}
@Override
public boolean tap(float x, float y, int count) {
ThreadUtil.invokeInGameThread(new Runnable() { //must invoke in game thread in case a dialog needs to be shown

View File

@@ -45,13 +45,21 @@ public class VPlayerPanel extends FContainer {
private final Map<ZoneType, InfoTab> zoneTabs = new HashMap<ZoneType, InfoTab>();
private final List<InfoTab> tabs = new ArrayList<InfoTab>();
private InfoTab selectedTab;
private float avatarHeight = VAvatar.HEIGHT;
private float displayAreaHeightFactor = 1.0f;
private boolean forMultiPlayer = false;
public VPlayerPanel(PlayerView player0, boolean showHand) {
public VPlayerPanel(PlayerView player0, boolean showHand, int playerCount) {
player = player0;
phaseIndicator = add(new VPhaseIndicator());
if(playerCount>2){
forMultiPlayer=true;
avatarHeight *= 0.5f;
//displayAreaHeightFactor *= 0.7f;
}
field = add(new VField(player));
avatar = add(new VAvatar(player));
avatar = add(new VAvatar(player, avatarHeight));
lblLife = add(new LifeLabel());
addZoneDisplay(ZoneType.Hand, FSkinImage.HAND);
addZoneDisplay(ZoneType.Graveyard, FSkinImage.GRAVEYARD);
@@ -200,23 +208,27 @@ public class VPlayerPanel extends FContainer {
}
//layout for bottom panel by default
float x = VAvatar.WIDTH;
float w = width - VAvatar.WIDTH;
float h = phaseIndicator.getPreferredHeight(w);
float x = avatarHeight;
float w = width - avatarHeight;
float indicatorScale = 1f;
if(avatarHeight<VAvatar.HEIGHT){
indicatorScale = 0.6f;
}
float h = phaseIndicator.getPreferredHeight(w) * indicatorScale;
phaseIndicator.setBounds(x, height - h, w, h);
float y = height - VAvatar.HEIGHT;
float displayAreaHeight = y / 3;
float y = height - avatarHeight;
float displayAreaHeight = displayAreaHeightFactor * y / 3;
y -= displayAreaHeight;
for (InfoTab tab : tabs) {
tab.displayArea.setBounds(0, y, width, displayAreaHeight);
}
y = height - VAvatar.HEIGHT;
y = height - avatarHeight;
avatar.setPosition(0, y);
float lifeLabelWidth = LIFE_FONT.getBounds("99").width * 1.2f; //make just wide enough for 2-digit life totals
float infoLabelHeight = VAvatar.HEIGHT - phaseIndicator.getHeight();
float lifeLabelWidth = LIFE_FONT.getBounds("99").width * 1.2f * indicatorScale; //make just wide enough for 2-digit life totals
float infoLabelHeight = avatarHeight - phaseIndicator.getHeight();
lblLife.setBounds(x, y, lifeLabelWidth, infoLabelHeight);
x += lifeLabelWidth;
@@ -461,9 +473,11 @@ public class VPlayerPanel extends FContainer {
else {
g.startClip(-1, y, w + 2, yAcross - y);
}
g.drawLine(1, MatchScreen.BORDER_COLOR, 0, yAcross, w, yAcross);
g.drawLine(1, MatchScreen.BORDER_COLOR, 0, y, 0, h);
g.drawLine(1, MatchScreen.BORDER_COLOR, w, y, w, h);
if(forMultiPlayer) {
g.drawLine(1, MatchScreen.BORDER_COLOR, 0, yAcross, w, yAcross);
g.drawLine(1, MatchScreen.BORDER_COLOR, 0, y, 0, h);
g.drawLine(1, MatchScreen.BORDER_COLOR, w, y, w, h);
}
g.endClip();
}
}