package org.multiverseking.card;
import com.jme3.input.controls.ActionListener;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.simsilica.es.Entity;
import com.simsilica.es.EntityId;
import com.simsilica.es.EntitySet;
import java.util.ArrayList;
import java.util.HashMap;
import org.hexgridapi.core.coordinate.HexCoordinate;
import org.hexgridapi.core.mousepicking.GridMouseControlAppState;
import org.hexgridapi.events.MouseInputEvent;
import org.hexgridapi.events.MouseInputEvent.MouseInputEventType;
import org.hexgridapi.events.MouseRayListener;
import org.hexgridapi.events.TileInputListener;
import org.multiverseking.card.attribut.CardRenderPosition;
import static org.multiverseking.card.attribut.CardRenderPosition.DECK;
import static org.multiverseking.card.attribut.CardRenderPosition.FIELD;
import static org.multiverseking.card.attribut.CardRenderPosition.HAND;
import static org.multiverseking.card.attribut.CardRenderPosition.OUTERWORLD;
import org.multiverseking.card.gui.Card;
import org.multiverseking.card.gui.Hover;
import org.multiverseking.core.utility.EntitySystemAppState;
import org.multiverseking.field.collision.CollisionSystem;
import org.multiverseking.utility.component.EAttributComponent;
import org.multiverseking.loader.CardProperties;
import org.multiverseking.loader.EntityLoader;
import org.multiverseking.loader.UnitLoader;
import org.multiverseking.render.AbstractRender.RenderType;
import org.multiverseking.render.animation.Animation;
import org.multiverseking.render.animation.AnimationComponent;
import tonegod.gui.controls.windows.Window;
import tonegod.gui.core.Screen;
/**
* System used to render all card on the screen.
*
* @todo AbstractRender card on a better way.
* @todo AbstractRender the opponent hand, show how many card the opponent got
* in
* hand(opposite side).
* @author roah
*/
public class CardRenderSystem extends EntitySystemAppState implements MouseRayListener {
// <editor-fold defaultstate="collapsed" desc="Used Variable">
/**
* Screen used by the ToneGodGUI to displays cards on the screen.
*/
private Screen screen;
/**
* All card on the current hand.
*/
private HashMap<EntityId, Card> handCards = new HashMap<EntityId, Card>();
/**
* All card on the current field.
*/
private ArrayList<EntityId> fieldCards = new ArrayList<EntityId>();
/**
* Contain cards who have been removed from the field, played, destroyed
* from the hand or deck, etc...
*/
private ArrayList<EntityId> outerworldCards = new ArrayList<EntityId>();
/**
* Displays all cards in the opponent hand (backfaced).
*/
private ArrayList<Card> opponentHandWin = new ArrayList<Card>();
/**
* Contain all cards in the decks.
*/
private ArrayList<EntityId> deckCards = new ArrayList<EntityId>();
/**
* Diplays the Decks.
*/
private Window deckWin;
/**
* Displays lastPlayed Cards.
*/
private Window outerworldWin;
/**
* Displays a list of cards on the current field.
*/
private Window fieldWin;
/**
* Overlay used when a the mouse is over a card.
*/
private Hover hover;
/**
* Show properties of a card currenty previewed.
*
* @todo AbstractRender it on a better way.
*/
Window castDebug;
/**
* Used to put a card on from of other when mose is over it.
*/
private float zOrder;
/**
* Used to know the area on the screen where to drag a card to activate it.
*/
private Vector2f minCastArea;
private Vector2f maxCastArea;
/**
* Save the card castDebug on preview so we can put it back if not casted,
* otherwise we remove it.
*/
private Card cardPreviewCast;
// </editor-fold>
@Override
protected EntitySet initialiseSystem() {
this.screen = new Screen(app);
app.getGuiNode().addControl(screen);
hover = new Hover(screen);
minCastArea = new Vector2f(screen.getWidth() * 0.05f, screen.getHeight() * 0.2f);
maxCastArea = new Vector2f(screen.getWidth() * 0.90f, screen.getHeight() - (screen.getHeight() * 0.2f));
return entityData.getEntities(CardRenderComponent.class);
}
@Override
protected void addEntity(Entity e) {
addCardToScreen(e);
}
/**
* Add a new card corresponding to the entity on the screen.
*
* @param e
* @todo Handle Outerworld cards, show permanently a picture of the last
* card send to the outerworld. when mouse is over the outerworld Zone last
* cast send to the outerworld show up his stats. When clic on the area
* cards in the outerworld show up.
* @todo Handle Deck cards Backfaced bunch of cards, when mouse over, the
* number of card left in show up, got a timer on it to knwo when next cards
* will be picked up.
* @todo Handle Field cards Permanently show a picture of the last played
* cards (even opponent last played cards if visible) When mouse over, show
* cards counts on the field (opponent cards is counted but only those the
* player is able to see), when mouse over show the cards count by type and
* by possessor. when clic on field area zone, cards who are currently on
* the field and visible show up, when clic on one of these card, it zoom to
* the location of the card on the field
*/
private void addCardToScreen(Entity e) {
CardRenderPosition cardPos = e.get(CardRenderComponent.class).getRenderPosition();
if (cardPos == CardRenderPosition.HAND) {
String cardName = e.get(CardRenderComponent.class).getName();
Card card;
CardProperties properties = new EntityLoader(app).loadCardProperties(cardName,
e.get(CardRenderComponent.class).getRenderType());
if (properties != null) {
card = new Card(screen, true, cardName, handCards.size() - 1, e.getId(),
properties);
handCards.put(e.getId(), card);
screen.addElement(card);
card.resetHandPosition();
for (Card c : handCards.values()) {
c.setZOrder(c.getZOrder());
}
} else {
System.err.println("Card files cannot be locate. " + cardName);
entityData.removeComponent(e.getId(), CardRenderComponent.class);
}
} else {
modifyCardOnScreen(e.getId(), cardPos, new CardProperties());
}
}
@Override
protected void removeEntity(Entity e) {
removeCardFromScreen(e.getId());
}
/**
* Remove a card corresponding to the entity without knowing the
* CardRenderPosition of it.
*
* @param id
*/
private void removeCardFromScreen(EntityId id) {
CardRenderPosition[] screenPos = CardRenderPosition.values();
for (CardRenderPosition cardPos : screenPos) {
if (modifyCardOnScreen(id, cardPos, null)) {
return;
}
}
}
/**
* Remove/add a card corresponding to the entity when knowing the
* CardRenderPosition of it.
*
* @param id
* @param screenPos where the card is.
* @param properties Set to null to remove a card.
* @return true if the card have to be removed and have been removed.
*/
private boolean modifyCardOnScreen(EntityId id, CardRenderPosition screenPos, CardProperties properties) {
switch (screenPos) {
case DECK:
if (properties != null) {
deckCards.add(id);
} else {
if (deckCards.contains(id)) {
deckCards.remove(id);
return true;
}
}
break;
case FIELD:
if (properties != null) {
fieldCards.add(id);
} else {
if (fieldCards.contains(id)) {
fieldCards.remove(id);
return true;
}
}
break;
case HAND:
if (properties != null) {
Card card = new Card(screen, true, properties.getName(), handCards.size() - 1, id,
properties);
handCards.put(id, card);
screen.addElement(card);
card.resetHandPosition();
for (Card c : handCards.values()) {
c.setZOrder(c.getZOrder());
}
} else {
if (handCards.containsKey(id)) {
Card card = handCards.get(id);
screen.removeElement(card);
handCards.remove(id);
return true;
}
}
break;
case OUTERWORLD:
if (properties != null) {
outerworldCards.add(id);
} else {
if (outerworldCards.contains(id)) {
outerworldCards.remove(id);
return true;
}
}
break;
default:
throw new UnsupportedOperationException(screenPos
+ " CardPosition isn't supported by " + this.getClass().getName());
}
return false;
}
@Override
protected void updateEntity(Entity e) {
removeCardFromScreen(e.getId());
addCardToScreen(e);
}
/**
* @todo Get the mouse position to see if on the position of a cards (better
* than the current hover ?)
*/
@Override
protected void updateSystem(float tpf) {
}
/**
* Called when the mouse is over a card.
*
* @param card the mouse is.
*/
public void hasFocus(Card card) {
zOrder = card.getZOrder();
screen.updateZOrder(card);
hover.setProperties(card.getProperties());
card.addChild(hover);
}
/**
* Called when a card lost the focus.
*
* @param card who lost the focus.
*/
public void lostFocus(Card card) {
hover.removeAllChildren();
card.removeChild(hover);
card.setZOrder(zOrder);
}
public void isInCastArea(Card card) {
if (screen.getMouseXY().x > minCastArea.x && screen.getMouseXY().x < maxCastArea.x
&& screen.getMouseXY().y > minCastArea.y && screen.getMouseXY().y < maxCastArea.y
&& cardPreviewCast == null) {
castPreview(card);
}
}
/**
* @todo Add the cast effect activation on the field.
* @param card
*/
private void castPreview(Card card) {
cardPreviewCast = card;
if (activateCard(null)) {
if (castDebug == null) {
castDebug = new Window(screen, "CastDebug", new Vector2f(175, 155), new Vector2f(250, 20));
castDebug.setMinDimensions(new Vector2f(200, 26));
castDebug.setIgnoreMouse(true);
}
castDebug.setText(" " + card.getCardName() + " preview cast !");
screen.addElement(castDebug);
screen.removeElement(card);
//Register the input for the card system
app.getStateManager().getState(GridMouseControlAppState.class).register(this);
app.getInputManager().addListener(cardInputListener, MouseInputEventType.RMB.toString());
}
}
private void closePreview() {
activateCard(null);
cardPreviewCast = null;
screen.removeElement(screen.getElementById(castDebug.getUID()));
//Remove the input for the card system
app.getStateManager().getState(GridMouseControlAppState.class).register(this);
app.getInputManager().removeListener(cardInputListener);
}
private void castCanceled() {
screen.addElement(cardPreviewCast);
cardPreviewCast.setZOrder(zOrder);
closePreview();
}
private void activateCard(HexCoordinate castCoord, RenderType type) {
switch (type) {
case Core:
break;
case Debug:
break;
case Environment:
break;
case Titan:
break;
case Unit:
CardRenderComponent cardRender = entities.getEntity(cardPreviewCast.getCardEntityUID()).get(CardRenderComponent.class);
String name = cardRender.getName();
UnitLoader unitLoader = new EntityLoader(app).loadUnitStats(name);
if (unitLoader != null) {
/* @todo How this is suppose to work without render Comp !? */
entityData.setComponents(cardPreviewCast.getCardEntityUID(),
// new HexPositionComponent(castCoord, Rotation.A),
cardRender.clone(CardRenderPosition.FIELD),
new AnimationComponent(Animation.SUMMON),
new EAttributComponent(cardPreviewCast.getProperties().getElement()),
unitLoader.getCollisionComponent(),
unitLoader.getInitialStatsComponent());
}
break;
default:
throw new UnsupportedOperationException(type.name() + " isn't a supported cardType.");
}
}
/**
* Activate the HexMapInput on pulse Mode if (event == null &&
* cardPreviewCast != null), Desactivate the HexMapInput pulseMode if (event
* != null && cardPreview != null)
*
* @param event result when a leftMouse event happen on hexMap.
*/
private boolean activateCard(MouseInputEvent event) {
/**
* If a card is currently in Casting Preview we check if it can be
* casted, No card is currently activated so we switch over all card
* type to know what preview to activate. (the entity will be removed
* from this system automaticaly if he have to)
*/
if (cardPreviewCast != null) {
if (event == null) {
/**
* We activate the pulse Mode, if not activated the cast is
* canceled.
*/
if (!app.getStateManager().getState(GridMouseControlAppState.class).setCursorPulseMode(this)) {
return false;
}
} else if (cardPreviewCast.getProperties().getRenderType().equals(RenderType.Titan)) {
/**
* We check if the collision system is currently running, if
* it's not the card will be directly casted.
*/
CollisionSystem collisionSystem = app.getStateManager().getState(CollisionSystem.class);
if (collisionSystem != null) {
if (collisionSystem.isValidPosition(event.getPosition(),
cardPreviewCast.getProperties().getRenderType())) {
activateCard(event.getPosition(), cardPreviewCast.getProperties().getRenderType());
closePreview();
} else {
castCanceled();
}
} else {
activateCard(event.getPosition(), cardPreviewCast.getProperties().getRenderType());
closePreview();
}
}
}
return true;
}
private ActionListener cardInputListener = new ActionListener() {
@Override
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals(MouseInputEventType.RMB.toString()) && !keyPressed) {
castCanceled();
}
}
};
@Override
public MouseInputEvent MouseRayInputAction(MouseInputEvent.MouseInputEventType mouseInputType, Ray ray) {
return null;
}
@Override
public void onMouseAction(MouseInputEvent event) {
if (event.getType().equals(MouseInputEventType.LMB)) {
activateCard(event);
}
}
/**
* Check if the player got cards in is hand.
*
* @return true if there is card in the player hand.
*/
public boolean gotCardInHand() {
return !handCards.isEmpty();
}
/**
* hide all card on the current System.
*
* @todo Extends to affect the whole system GUI.
*/
public void hideCards() {
showCards(null, false);
}
/**
* Show all card on the current System.
*
* @todo Extends to affect the whole system GUI.
*/
public void showCards() {
showCards(null, true);
}
/**
* Show/hide card on the selected position in the system, Set to null to
* show/hide all cards.
*
* @param show Set to true to show, false to hide.
* @param screenPosition
*/
public void showCards(CardRenderPosition screenPosition, boolean show) {
if (screenPosition == null) {
if (!handCards.isEmpty()) {
for (Card card : handCards.values()) {
card.show();
}
}
/**
* @todo: other position.
*/
return;
}
switch (screenPosition) {
case DECK:
/**
* @todo
*/
break;
case FIELD:
/**
* @todo
*/
break;
case HAND:
if (!handCards.isEmpty()) {
for (Card card : handCards.values()) {
if (show) {
card.show();
} else {
card.hide();
}
}
}
break;
case OUTERWORLD:
/**
* @todo
*/
break;
}
}
@Override
protected void cleanupSystem() {
hover.removeAllChildren();
hover = null;
for (Card card : handCards.values()) {
screen.removeElement(card);
}
handCards.clear();
app.getGuiNode().removeControl(screen);
screen = null;
}
}