package com.cardshifter.gdx.screens; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Screen; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.cardshifter.api.incoming.UseAbilityMessage; import com.cardshifter.api.messages.Message; import com.cardshifter.api.outgoing.*; import com.cardshifter.gdx.*; import com.cardshifter.gdx.ui.CardshifterClientContext; import com.cardshifter.gdx.ui.EntityView; import com.cardshifter.gdx.ui.PlayerView; import com.cardshifter.gdx.ui.cards.CardView; import com.cardshifter.gdx.ui.cards.CardViewSmall; import com.cardshifter.gdx.ui.zones.CompactHiddenZoneView; import com.cardshifter.gdx.ui.zones.DefaultZoneView; import com.cardshifter.gdx.ui.zones.ZoneView; import java.util.*; import java.util.List; /** * Created by Simon on 1/31/2015. */ public class GameScreen implements Screen, TargetableCallback { private final CardshifterGame game; private final CardshifterClient client; private final int playerIndex; private final int gameId; private final Table table; private final Map<Integer, ZoneView> zoneViews = new HashMap<Integer, ZoneView>(); private final Map<Integer, EntityView> entityViews = new HashMap<Integer, EntityView>(); private final Map<String, Container<Actor>> holders = new HashMap<String, Container<Actor>>(); private final List<EntityView> targetsSelected = new ArrayList<EntityView>(); private final Screen parentScreen; private AvailableTargetsMessage targetsAvailable; private final TargetableCallback onTarget = new TargetableCallback() { @Override public boolean addEntity(EntityView view) { if (targetsSelected.contains(view)) { targetsSelected.remove(view); Gdx.app.log("GameScreen", "Removing selection " + view.getId()); view.setTargetable(TargetStatus.TARGETABLE, this); return false; } if (targetsAvailable != null && targetsAvailable.getMax() == 1 && targetsAvailable.getMin() == 1) { Gdx.app.log("GameScreen", "Sending selection " + view.getId()); client.send(new UseAbilityMessage(gameId, targetsAvailable.getEntity(), targetsAvailable.getAction(), new int[]{ view.getId() })); return false; } Gdx.app.log("GameScreen", "Adding selection " + view.getId()); view.setTargetable(TargetStatus.TARGETED, this); return targetsSelected.add(view); } }; private final CardshifterClientContext context; //private final float screenWidth; private final float screenHeight; public GameScreen(final CardshifterGame game, final CardshifterClient client, NewGameMessage message, final Screen parentScreen) { this.parentScreen = parentScreen; this.game = game; this.client = client; this.playerIndex = message.getPlayerIndex(); this.gameId = message.getGameId(); this.context = new CardshifterClientContext(game.skin, message.getGameId(), client, game.stage); //this.screenWidth = CardshifterGame.STAGE_WIDTH; this.screenHeight = CardshifterGame.STAGE_HEIGHT; this.table = new Table(game.skin); Table leftTable = new Table(game.skin); Table topTable = new Table(game.skin); //Table rightTable = new Table(game.skin); Table centerTable = new Table(game.skin); TextButton backToMenu = new TextButton("Back to menu", game.skin); backToMenu.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { game.setScreen(parentScreen); } }); leftTable.add(backToMenu).expandX().fill().row(); addZoneHolder(leftTable, 1 - this.playerIndex, "").expandY().fillY(); addZoneHolder(leftTable, this.playerIndex, "").expandY().fillY(); leftTable.add("controls").row(); TextButton actionDone = new TextButton("Done", game.skin); actionDone.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { if (targetsAvailable != null) { int selected = targetsSelected.size(); if (selected >= targetsAvailable.getMin() && selected <= targetsAvailable.getMax()) { int[] targets = new int[targetsSelected.size()]; for (int i = 0; i < targets.length; i++) { targets[i] = targetsSelected.get(i).getId(); } UseAbilityMessage message = new UseAbilityMessage(gameId, targetsAvailable.getEntity(), targetsAvailable.getAction(), targets); client.send(message); } } } }); leftTable.add(actionDone); topTable.add(leftTable).left().expandY().fillY(); topTable.add(centerTable).center().expandX().expandY().fill(); //topTable.add(rightTable).right().width(150).expandY().fillY(); addZoneHolder(centerTable, 1 - this.playerIndex, "Hand").top().height(this.screenHeight/4); addZoneHolder(centerTable, 1 - this.playerIndex, "Battlefield").height(this.screenHeight/4); addZoneHolder(centerTable, this.playerIndex, "Battlefield").height(this.screenHeight/4); this.table.add(topTable).expand().fill().row(); addZoneHolder(this.table, this.playerIndex, "Hand").height(140).expandX().fill(); this.table.setFillParent(true); } private Cell<Container<Actor>> addZoneHolder(Table table, int i, String name) { Container<Actor> container = new Container<Actor>(); container.setName(name); // container.fill(); Cell<Container<Actor>> cell = table.add(container).expandX().fillX(); table.row(); holders.put(i + name, container); return cell; } @Override public void render(float delta) { } @Override public void resize(int width, int height) { } @Override public void show() { game.stage.addActor(table); } @Override public void hide() { table.remove(); } @Override public void pause() { } @Override public void resume() { } @Override public void dispose() { } public Map<Class<? extends Message>, SpecificHandler<?>> getHandlers() { Map<Class<? extends Message>, SpecificHandler<?>> handlers = new HashMap<Class<? extends Message>, SpecificHandler<?>>(); handlers.put(AvailableTargetsMessage.class, new SpecificHandler<AvailableTargetsMessage>() { @Override public void handle(AvailableTargetsMessage message) { targetsAvailable = message; targetsSelected.clear(); for (EntityView view : entityViews.values()) { view.setTargetable(TargetStatus.NOT_TARGETABLE, onTarget); } for (int id : message.getTargets()) { EntityView view = entityViews.get(id); if (view != null) { view.setTargetable(TargetStatus.TARGETABLE, onTarget); } } } }); handlers.put(UsableActionMessage.class, new SpecificHandler<UsableActionMessage>() { @Override public void handle(UsableActionMessage message) { int id = message.getId(); EntityView view = entityViews.get(id); if (view != null) { view.usableAction(message); if (view instanceof CardViewSmall) { ((CardViewSmall)view).setUsable(GameScreen.this); } } } }); handlers.put(CardInfoMessage.class, new SpecificHandler<CardInfoMessage>() { @Override public void handle(CardInfoMessage message) { ZoneView zone = getZoneView(message.getZone()); if (zone != null) { zone.removeCard(message.getId()); } EntityView entityView = entityViews.remove(message.getId()); if (entityView != null) { entityView.remove(); } if (zone != null) { entityViews.put(message.getId(), zone.addCard(message)); } } }); handlers.put(EntityRemoveMessage.class, new SpecificHandler<EntityRemoveMessage>() { @Override public void handle(EntityRemoveMessage message) { EntityView view = entityViews.get(message.getEntity()); for (ZoneView zone : zoneViews.values()) { if (zone.hasCard(message.getEntity())) { zone.removeCard(message.getEntity()); } } if (view != null) { view.entityRemoved(); entityViews.remove(message.getEntity()); } } }); handlers.put(GameOverMessage.class, new SpecificHandler<GameOverMessage>() { @Override public void handle(GameOverMessage message) { Dialog dialog = new Dialog("Game Over!", context.getSkin()) { @Override protected void result(Object object) { game.setScreen(parentScreen); } }; dialog.button("OK"); dialog.show(context.getStage()); } }); handlers.put(PlayerMessage.class, new SpecificHandler<PlayerMessage>() { @Override public void handle(PlayerMessage message) { PlayerView playerView = new PlayerView(context, message); entityViews.put(message.getId(), playerView); Container<Actor> holder = holders.get(String.valueOf(message.getIndex())); if (holder != null) { holder.setActor(playerView.getActor()); } } }); handlers.put(ResetAvailableActionsMessage.class, new SpecificHandler<ResetAvailableActionsMessage>() { @Override public void handle(ResetAvailableActionsMessage message) { for (EntityView view : entityViews.values()) { view.setTargetable(TargetStatus.NOT_TARGETABLE, null); view.clearUsableActions(); } } }); handlers.put(UpdateMessage.class, new SpecificHandler<UpdateMessage>() { @Override public void handle(UpdateMessage message) { EntityView entityView = entityViews.get(message.getId()); if (entityView != null) { entityView.set(message.getKey(), message.getValue()); } } }); handlers.put(ZoneChangeMessage.class, new SpecificHandler<ZoneChangeMessage>() { @Override public void handle(ZoneChangeMessage message) { ZoneView oldZone = getZoneView(message.getSourceZone()); // can be null ZoneView destinationZone = getZoneView(message.getDestinationZone()); int id = message.getEntity(); CardView entityView = (CardView) entityViews.remove(id); // can be null if (oldZone != null) { oldZone.removeCard(id); } if (destinationZone != null) { CardView newCardView = destinationZone.addCard(new CardInfoMessage(message.getDestinationZone(), id, entityView == null ? null : entityView.getInfo())); if (entityView != null) { entityView.zoneMove(message, destinationZone, newCardView); } entityViews.put(id, newCardView); } else { if (entityView != null) { entityView.zoneMove(message, destinationZone, null); } } /* Send to AI Medium: ZoneChangeMessage [entity=95, sourceZone=72, destinationZone=73] Send to AI Medium: CardInfo: 95 in zone 73 - {SCRAP=1, TAUNT=1, MAX_HEALTH=1, SICKNESS=1, MANA_COST=2, name=The Chopper, ATTACK=2, creatureType=Mech, HEALTH=1, ATTACK_AVAILABLE=1} Send to Zomis: ZoneChangeMessage [entity=95, sourceZone=72, destinationZone=73] if card is already known, send ZoneChange only if card is not known, send ZoneChange first and then CardInfo when cards are created from nowhere, ZoneChange with source -1 is sent and then CardInfo */ } }); handlers.put(ZoneMessage.class, new SpecificHandler<ZoneMessage>() { @Override public void handle(ZoneMessage message) { Gdx.app.log("GameScreen", "Zone " + message); ZoneView zoneView = createZoneView(message); if (zoneView != null) { PlayerView view = (PlayerView) entityViews.get(message.getOwner()); if (view == null) { Gdx.app.log("GameScreen", "no playerView for " + message.getOwner()); return; } String key = view.getIndex() + message.getName(); Container<Actor> container = holders.get(key); if (container == null) { Gdx.app.log("GameScreen", "no container for " + key); return; } Gdx.app.log("GameScreen", "putting zoneview for " + key); container.setActor(zoneView.getActor()); zoneViews.put(message.getId(), zoneView); } } }); return handlers; } private ZoneView createZoneView(ZoneMessage message) { String type = message.getName(); if (type.equals("Battlefield")) { return new DefaultZoneView(context, message, this.entityViews); } if (type.equals("Hand")) { return new DefaultZoneView(context, message, this.entityViews); } if (type.equals("Deck")) { return new CompactHiddenZoneView(game, message); } if (type.equals("Cards")) { return null; // Card models only } throw new RuntimeException("Unknown ZoneView type: " + message.getName()); } private ZoneView getZoneView(int id) { return this.zoneViews.get(id); } public boolean checkCardDrop(CardViewSmall cardView) { Table table = (Table)cardView.getActor(); Vector2 stageLoc = table.localToStageCoordinates(new Vector2()); Rectangle tableRect = new Rectangle(stageLoc.x, stageLoc.y, table.getWidth(), table.getHeight()); for (Container<Actor> actor : this.holders.values()) { if (actor.getName() == "Battlefield") { Vector2 stageBattlefieldLoc = actor.localToStageCoordinates(new Vector2(actor.getActor().getX(), actor.getActor().getY())); Vector2 modifiedSBL = new Vector2(stageBattlefieldLoc.x - actor.getWidth()/2, stageBattlefieldLoc.y - actor.getHeight()/2); Rectangle deckRect = new Rectangle(modifiedSBL.x, modifiedSBL.y, actor.getWidth() * 0.8f, actor.getHeight()); //uncomment this to see the bug where battlefields pop up in strange places /* Image squareImage = new Image(new Texture(Gdx.files.internal("cardbg.png"))); squareImage.setPosition(modifiedSBL.x, modifiedSBL.y); squareImage.setSize(deckRect.width, deckRect.height); this.game.stage.addActor(squareImage); */ if (tableRect.overlaps(deckRect)) { //this.addEntity(cardView); System.out.println("target found!"); return true; } } } return false; //these can be used to double check the location of the rectangles /* Image squareImage = new Image(new Texture(Gdx.files.internal("cardbg.png"))); squareImage.setPosition(modifiedSBL.x, modifiedSBL.y); squareImage.setSize(deckRect.width, deckRect.height); this.game.stage.addActor(squareImage); */ /* Image squareImage = new Image(new Texture(Gdx.files.internal("cardbg.png"))); squareImage.setPosition(stageLoc.x, stageLoc.y); squareImage.setSize(tableRect.width, tableRect.height); this.game.stage.addActor(squareImage); */ } @Override public boolean addEntity(EntityView view) { //called by the CardViewSmall when not in mulligan mode, nothing will happen return false; } }