GUI: stack - added card icon with targets info (announced and triggered targets, target choices);

This commit is contained in:
Oleg Agafonov 2023-12-23 16:26:37 +04:00
parent caf35c88c9
commit 9ad456239e
6 changed files with 152 additions and 81 deletions

View file

@ -45,7 +45,7 @@ import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class CardView extends SimpleCardView {
@ -441,48 +441,6 @@ public class CardView extends SimpleCardView {
controlledByOwner = false;
}
}
// card icons for permanents on battlefield
// icon - all from abilities
permanent.getAbilities(game).forEach(ability -> {
this.cardIcons.addAll(ability.getIcons(game));
});
// icon - face down
if (permanent.isFaceDown(game)) {
this.cardIcons.add(CardIconImpl.FACE_DOWN);
}
// icon - commander
if (game != null) {
Player owner = game.getPlayer(game.getOwnerId(permanent));
if (owner != null && game.isCommanderObject(owner, permanent)) {
this.cardIcons.add(CardIconImpl.COMMANDER);
}
}
// icon - ring-bearer
if (permanent.isRingBearer()) {
this.cardIcons.add(CardIconImpl.RINGBEARER);
}
// icon - restrictions (search it in card hints)
List<String> restricts = new ArrayList<>();
this.rules.forEach(r -> {
if (r.startsWith(HintUtils.HINT_ICON_RESTRICT)
|| r.startsWith(HintUtils.HINT_ICON_REQUIRE)) {
restricts.add(r
.replace(HintUtils.HINT_ICON_RESTRICT, "")
.replace(HintUtils.HINT_ICON_REQUIRE, "")
.trim()
);
}
});
if (!restricts.isEmpty()) {
restricts.sort(String::compareTo);
this.cardIcons.add(new CardIconImpl(CardIconType.OTHER_HAS_RESTRICTIONS, String.join("<br>", restricts)));
}
} else {
if (card.isCopy()) {
this.mageObjectType = MageObjectType.COPY_CARD;
@ -499,25 +457,6 @@ public class CardView extends SimpleCardView {
}
}
// card icons for any permanents and cards
if (game != null) {
// x cost
Zone cardZone = game.getState().getZone(card.getId());
if (card.getManaCost().containsX()
&& card.getSpellAbility() != null
&& (cardZone.match(Zone.BATTLEFIELD) || cardZone.match(Zone.STACK))) {
int costX;
if (card instanceof Permanent) {
// permanent on battlefield (can show x icon multiple turns, so use end_game source)
costX = ManacostVariableValue.END_GAME.calculate(game, card.getSpellAbility(), null);
} else {
// other like Stack (can show x icon on stack only, so use normal source)
costX = ManacostVariableValue.REGULAR.calculate(game, card.getSpellAbility(), null);
}
this.cardIcons.add(CardIconImpl.variableCost(costX));
}
}
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = new ArrayList<>(card.getCardType(game));
@ -647,6 +586,141 @@ public class CardView extends SimpleCardView {
// Get starting defense
this.startingDefense = CardUtil.convertLoyaltyOrDefense(card.getStartingDefense());
// add card icons at the end, so it will have full card view data
this.generateCardIcons(null, card, game);
}
/**
* Generate card icons for current object (support card, permanent or stack ability)
*
* @param ability only for stack ability, all other must use null
* @param object original card/permanent/source
*/
final protected void generateCardIcons(Ability ability, MageObject object, Game game) {
if (object instanceof Permanent) {
this.generateCardIconsForPermanent((Permanent) object, game);
}
this.generateCardIconsForAny(object, ability, game);
}
private void generateCardIconsForPermanent(Permanent permanent, Game game) {
// card icons for permanents on battlefield
if (game == null) {
return;
}
// icon - all from abilities
permanent.getAbilities(game).forEach(ability -> {
this.cardIcons.addAll(ability.getIcons(game));
});
// icon - face down
if (permanent.isFaceDown(game)) {
this.cardIcons.add(CardIconImpl.FACE_DOWN);
}
// icon - commander
Player owner = game.getPlayer(game.getOwnerId(permanent));
if (owner != null && game.isCommanderObject(owner, permanent)) {
this.cardIcons.add(CardIconImpl.COMMANDER);
}
// icon - ring-bearer
if (permanent.isRingBearer()) {
this.cardIcons.add(CardIconImpl.RINGBEARER);
}
// icon - restrictions (search it in card hints)
List<String> restricts = new ArrayList<>();
this.rules.forEach(r -> {
if (r.startsWith(HintUtils.HINT_ICON_RESTRICT)
|| r.startsWith(HintUtils.HINT_ICON_REQUIRE)) {
restricts.add(r
.replace(HintUtils.HINT_ICON_RESTRICT, "")
.replace(HintUtils.HINT_ICON_REQUIRE, "")
.trim()
);
}
});
if (!restricts.isEmpty()) {
restricts.sort(String::compareTo);
this.cardIcons.add(new CardIconImpl(CardIconType.OTHER_HAS_RESTRICTIONS, String.join("<br>", restricts)));
}
}
private void generateCardIconsForAny(MageObject object, Ability ability, Game game) {
if (game == null) {
return;
}
Card showCard = (object instanceof Card) ? (Card) object : null;
Zone showZone;
if (ability instanceof StackAbility) {
showZone = Zone.STACK;
} else {
showZone = game.getState().getZone(object.getId());
}
if (showZone == null) {
return;
}
Ability showAbility;
if (ability != null) {
showAbility = ability;
} else if (showCard != null) {
showAbility = showCard.getSpellAbility();
} else {
showAbility = null;
}
// icon - x cost
if (showCard != null
&& showCard.getManaCost().containsX()
&& showAbility != null
&& (showZone.match(Zone.BATTLEFIELD) || showZone.match(Zone.STACK))) {
int costX;
if (showCard instanceof Permanent) {
// permanent on battlefield (can show x icon multiple turns, so use end_game source)
costX = ManacostVariableValue.END_GAME.calculate(game, showAbility, null);
} else {
// other like Stack (can show x icon on stack only, so use normal source)
costX = ManacostVariableValue.REGULAR.calculate(game, showAbility, null);
}
this.cardIcons.add(CardIconImpl.variableCost(costX));
}
// icon - targets in stack
if (showZone.match(Zone.STACK) && this.getTargets() != null && !this.getTargets().isEmpty()) {
List<String> targets = new ArrayList<>();
this.getTargets()
.stream()
.map(t -> {
String info;
MageObject targetObject = game.getObject(t);
if (targetObject != null) {
info = targetObject.getIdName();
} else {
Player targetPlayer = game.getPlayer(t);
if (targetPlayer != null) {
info = targetPlayer.getName();
} else {
info = "Unknown";
}
}
return info;
})
.sorted()
.forEach(targets::add);
this.cardIcons.add(new CardIconImpl(
CardIconType.OTHER_HAS_TARGETS,
String.format("Has %d target(s). Move mouse over card to see target arrows:", this.getTargets().size())
+ "<br><br>" + String.join("<br>", targets),
"T-" + this.getTargets().size()
));
}
}
public CardView(MageObject object, Game game) {
@ -1350,6 +1424,7 @@ public class CardView extends SimpleCardView {
public boolean isExtraDeckCard() {
return this.extraDeckCard;
}
public boolean isLand() {
return cardTypes.contains(CardType.LAND);
}

View file

@ -30,7 +30,7 @@ import java.io.Serializable;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class GameView implements Serializable {
@ -91,12 +91,12 @@ public class GameView implements Serializable {
if (object != null) {
if (object instanceof Permanent) {
boolean controlled = ((Permanent) object).getControllerId().equals(createdForPlayerId);
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), new CardView(((Permanent) object), game, controlled, false, false)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, new CardView(((Permanent) object), game, controlled, false, false)));
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), new CardView(card, game, false, false, false)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), card, new CardView(card, game, false, false, false)));
}
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", new CardView(card, game)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, "", card, new CardView(card, game)));
}
if (card.isTransformable()) {
updateLatestCardView(game, card, stackObject.getId());
@ -105,7 +105,7 @@ public class GameView implements Serializable {
} else if (object != null) {
if (object instanceof PermanentToken) {
PermanentToken token = (PermanentToken) object;
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, token.getName(), new CardView(token, game)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, token.getName(), token, new CardView(token, game)));
checkPaid(stackObject.getId(), (StackAbility) stackObject);
} else if (object instanceof Emblem) {
CardView cardView = new CardView(new EmblemView((Emblem) object));
@ -113,24 +113,24 @@ public class GameView implements Serializable {
stackObject.setName(object.getName());
// ((StackAbility) stackObject).setExpansionSetCode(sourceCard.getExpansionSetCode());
stack.put(stackObject.getId(),
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView));
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, cardView));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else if (object instanceof Dungeon) {
CardView cardView = new CardView(new DungeonView((Dungeon) object));
stackObject.setName(object.getName());
stack.put(stackObject.getId(),
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView));
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, cardView));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else if (object instanceof Plane) {
CardView cardView = new CardView(new PlaneView((Plane) object));
stackObject.setName(object.getName());
stack.put(stackObject.getId(),
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView));
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), object, cardView));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else if (object instanceof Designation) {
Designation designation = (Designation) game.getObject(object.getId());
if (designation != null) {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), new CardView(designation, game)));
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, designation.getName(), designation, new CardView(designation, game)));
} else {
LOGGER.fatal("Designation object not found: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
}

View file

@ -35,11 +35,11 @@ public class StackAbilityView extends CardView {
// selectable, chooseable, card icons etc. Search by getSourceCard
private final CardView sourceCard;
public StackAbilityView(Game game, StackAbility ability, String sourceName, CardView sourceCard) {
public StackAbilityView(Game game, StackAbility ability, String sourceName, MageObject sourceObject, CardView sourceView) {
this.id = ability.getId();
this.mageObjectType = MageObjectType.ABILITY_STACK;
this.abilityType = ability.getStackAbility().getAbilityType();
this.sourceCard = sourceCard;
this.sourceCard = sourceView;
this.sourceCard.setMageObjectType(mageObjectType);
this.name = "Ability";
this.loyalty = "";
@ -79,12 +79,7 @@ public class StackAbilityView extends CardView {
updateTargets(game, ability);
// card icons (warning, it must be synced in gui dialogs with replaced card, see comments at the start of the file)
// cost x
if (ability.getManaCostsToPay().containsX()) {
int costX = ManacostVariableValue.END_GAME.calculate(game, ability, null);
this.cardIcons.add(CardIconImpl.variableCost(costX));
}
this.generateCardIcons(ability, sourceObject, game);
}
private void updateTargets(Game game, StackAbility ability) {

View file

@ -309,7 +309,7 @@ public final class Main {
try {
MageServer testServer = (MageServer) TransporterClient.createTransporterClient(serverLocator.getLocatorURI(), MageServer.class, metadata);
if (testServer != null) {
testServer.getServerState();
testServer.getServerState(); // check connection
return true;
}
} catch (Throwable t) {

View file

@ -21,7 +21,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public class UserManagerImpl implements UserManager {
// timeouts on user's activity (on connection problems)
private static final int USER_CONNECTION_TIMEOUTS_CHECK_SECS = 30; // TODO: replace to 60 before merge
private static final int USER_CONNECTION_TIMEOUTS_CHECK_SECS = 30;
private static final int USER_CONNECTION_TIMEOUT_INFORM_AFTER_SECS = 30; // inform user's opponents about problem
private static final int USER_CONNECTION_TIMEOUT_SESSION_EXPIRE_AFTER_SECS = 3 * 60; // session expire - remove from all tables and chats (can't reconnect after it)
private static final int USER_CONNECTION_TIMEOUT_REMOVE_FROM_SERVER_SECS = 8 * 60; // removes from users list

View file

@ -35,6 +35,7 @@ public enum CardIconType {
OTHER_FACEDOWN("prepared/reply-fill.svg", CardIconCategory.ABILITY, 100),
OTHER_COST_X("prepared/square-fill.svg", CardIconCategory.ABILITY, 100),
OTHER_HAS_RESTRICTIONS("prepared/exclamation-triangle-fill.svg", CardIconCategory.ABILITY, 100),
OTHER_HAS_TARGETS("prepared/square-fill.svg", CardIconCategory.COMMANDER, 100),
//
RINGBEARER("prepared/ring.svg", CardIconCategory.COMMANDER, 100),
COMMANDER("prepared/crown.svg", CardIconCategory.COMMANDER, 100), // TODO: fix big size, see CardIconsPanel