= programming
:author: 
:revnumber: 
:revdate: 2016/03/17 20:48
:relfileprefix: ../../../
:imagesdir: ../../..
ifdef::env-github,env-browser[:outfilesuffix: .adoc]



== CardGame - Programming


=== Introduction

This is the section descibled the detail process of Programming the card game in Java, Groovy

Almost the tasks are trivial and suite for beginers and newbies to JME3. 

FYI, some part require knowledge:

.  advanced Java concept such as multi-threading, serialize … Groovy such as Closure
.  JME3 like …AppState, picking… 
.  or knowledge of Atom framework 

which you should read before start here. The links help you can revise your knowledges:

<<jme3#, JME3 Advanced>>
<<jme3/scripting#, JME3 Scripting>>
<<jme3/advanced/atom_framework#, AtomFramework >>


=== Short list

Checklist of what we going to implementation in this tutorial:

*  GameStage and Gameplay
**  Card gameplay elements (Game, Turn, Phase,..)
**  GameWorld
**  Select/Picking
**  Start/pause
**  Save/load game states

*  Entities
**  Card
**  More

*  States
**  Menu
**  InGame
**  Loading

*  Managers
**  StageManager
**  GUIManager
**  GamePlayManager

*  Controls
**  SelectControl
**  CardEntityControl



=== GameStage and GamePlay

Here is where you clean up your game design, and write down a few most obvious Classes that build up the game.

*CardGame* : obvious

*Card* : represent a Card in the game

*CardPlayer* extends *Player*: a Player in Atom framework have basic ID, name, character…, already have built-in mechanism to go online -multiplayers if need!

*CardTable* extends *GameLevel*: represent the Table where two opponent sit and duel. Of course we can have different levels (in different Modes) like when Player traveling around and play in the future!

*CardMatch* : a Match can be held between two CardPlayer opponents

*Turn* : a Turn represent a Turn in this card game. Has index number and a point to a Player take that Turn. a Turn has 6 TurnPhase (s)

*TurnPhase* : DrawPhase, MainPhase, MainPhase2… contains a bunch of CardAction

*CardAction* : obvious. action taken of Player, like : add one more card in his hand and summon etc…

see the full implementation in the source code. Detailed explanation below!


=== Entities


==== Card

Consider the a Card as a most basic Entity.
[ Warning: Don't mistake I'm going to use any kind of Composing Entity System here! ].

An Card Entity should contains infomations relate to a real card in a card game, it also has a representation, which is a 3D Model in JME scenegraph. That’s why Card class extends SpatialEntity.

....
  SpatialEntity This is the handy parent class for classes in Atom framework which also holding Entity data, and has a Spatial as representation. Atom use a built-in two-way connection between the Entity – Spatial, a mechanism which helps you do almost anything you want with SpatialEntity (select-picking,enable/disable…ect)
....

Information relate to the orginal Magic card game:

[source,java]
----

String cardId=”"
String name = “”
String picture = “”

int attack = -1
int defend = -1
String cardType = “”

String attribute = “”

----

[…full in the source code.The meanings are obvious.] 

If you declare like this in Groovy, that mean each of these fields are a Property, which have basic generated Setter/Getter by Groovy compiler. We need one more attribute:
Card orgCard

This hold a link to the orginal Card in the CardLibrary, which keeps information of every card avaiable in this game. We will implement this class later. Card.groovy

[source,java]
----

package magiccard.gameplay
 
import com.jme3.scene.Spatial
import sg.atom.entity.SpatialEntity
/**
*
* @author cuong.nguyenmanh2
*/
public class Card  extends SpatialEntity{
    String cardId=”"
    String name = “”
    String picture = “”
 
    int attack = -1
    int defend = -1
    String cardType = “”
 
    String attribute = “”
    String cardSubType = “”
    int level = -1
 
    String summonRule = “”
    String effect = “”
    String desc = “”
    String longDesc = “”
    String character = “”
    String flipScript=”"
    String effectScript=”"
    String rarity=”"
    Card orgCard
 
    public Card(String name){
        super(name,name)
        this.name = name
    }
 
    public Card(Card orgCard){
        super(orgCard.name,orgCard.name)
        this.orgCard = orgCard
        this.cardId = orgCard.cardId
        this.name = orgCard.name
        this.picture = orgCard.picture
        this.attack = orgCard.attack
        this.defend = orgCard.defend
        this.cardType = orgCard.cardType
        this.level = orgCard.level
        this.desc = orgCard.desc
        this.longDesc = orgCard.longDesc
    }
}

----


==== Show the card

According to the Atom framework, the representation part of a Card should be provided by a EntityFactory. In this implementation, I just ask the GameLevel – The CardTable, to made it up, cause it’s really simple.

Now consider a Card spatial built from a flatten 3d box and 2 sides: front &amp; back with approriate textures title FrontTexture and BackTexture. In YugiGame, BackTexture are the same for all the Card, remember that we already set it in the SceneEditor! Now our job is to load the Card model and change the FrontTexture to the path in card.picture.

This is a part of the CardTable class which create and “attach” the Card spatial to the levelNode, add two more controls for movement handle and select handle. Simple isn’t it?

[source,java]
----

    public void createCardOrg(){
        cardOrg = ((Node) ((Node) assetManager.loadModel(“Models/Cards/Template/card1.j3o”)).getChild(“Card”)).getChild(“Cube1″);
    }
    Geometry createCard(Card card) {
        // extract the card info
        String path = card.picture

        // create the geometry
        Geometry newCard = (Geometry) cardOrg.clone();
        Material cloneMat = newCard.getMaterial().clone();
        cloneMat.setTexture(“ColorMap2″, assetManager.loadTexture(path));
        newCard.setMaterial(cloneMat);
        //cloneMat.setBoolean(“MixGlow”,true);
        levelNode.attachChild(newCard);
        card.spatial = newCard;
        newCard.setLocalScale(-0.4f,-0.4f,0.4f)
        // attach the control
        newCard.addControl(new CardSpatialControl(worldManager,card));
        newCard.addControl(new CardSelectControl(worldManager,gamePlayManager));
        return newCard;
    }

----


==== Card movement

....
  For this kind of game, I just want to introduce most basic movement form, Steering movements with brace, veclocity..etc will be introduced in next tuts. 
....

By basic movement forms, I want to talk about: 

....
– MOVETO: Move smoothly in a straight line from one point to another. 
– ROTTO: Rotate smoothly by Quaternion from one angle to another. 
– GIGGLE : Shaking, for example prepare to explode! 

....

These 3 basic movement types are already enough to compose quite fascinated effects if you know how to. See by your self in the video in the first post! The most normal and obvious way to put movements into a Spatial in JME3 is to make an Control, name it CardSpatialControl. 

[source,java]
----

package magiccard.gameplay

import com.jme3.scene.control.AbstractControl
import com.jme3.math.Vector3f
import com.jme3.math.FastMath;
import sg.atom.entity.SpatialEntityControl
import sg.atom.stage.SelectManager
import sg.atom.stage.WorldManager
import com.jme3.math.Quaternion
/**
 *
 * @author hungcuong
 */
public class CardSpatialControl extends SpatialEntityControl{

    boolean stopMove = false;
    boolean stopRot = false;
    boolean gigle = false;
    Vector3f targetPos;
    Vector3f oldPos;
    Quaternion oldRot;
    Quaternion targetRot;
    float timeRot = 0;
    float speedRot = 0.3f;
    float speedPos = 0.04f;

    public CardSpatialControl(WorldManager worldManager, Card card) {
        super(worldManager, card);
    }

    void pos(Vector3f targetPos){
        this.targetPos = targetPos.clone();;
        this.oldPos = spatial.getLocalTranslation().clone();
        stopMove = false;
    }
    void rot(Quaternion targetRot){
        this.targetRot = targetRot.clone();
        this.oldRot = spatial.getLocalRotation().clone();
        timeRot = 0;
        stopRot = false;
    } 

    public void controlUpdate(float tpf){
        updatePos(tpf);
        updateRot(tpf);
        if (gigle){
            updateGiggle();
        }
    }
    void updatePos(float tpf){
        if (targetPos!=null){
            Vector3f newPos;
            Vector3f currentPos = spatial.getLocalTranslation();
            float dis = currentPos.distance(targetPos)
            if (!stopMove){
                if ( dis> speedPos){
                    Vector3f force = targetPos.subtract(currentPos).normalize().mult(speedPos)
                    newPos = currentPos.add(force)
                } else {
                    newPos = targetPos.clone();
                    stopMove = true;

                }
                spatial.setLocalTranslation(newPos)
            }

        }
    }

    void updateRot(float tpf){
        if (targetRot!=null&&oldRot!=null){
            if (!stopRot){
                Quaternion newRot = new Quaternion();
                if ( timeRot <speedRot){
                    newRot.slerp(oldRot,targetRot,(float)(timeRot/speedRot))
                    timeRot += tpf;
                } else {
                    stopRot = true;
                    newRot.set(targetRot)
                }
                spatial.setLocalRotation(newRot);
            }
        }
    }

    void updateGiggle(){
        float x =(0.5f-FastMath.nextRandomFloat()) * 0.005f;
        float y =(0.5f-FastMath.nextRandomFloat()) * 0.005f;
        float z =0;
        Vector3f oldPos=spatial.getLocalTranslation().clone();
        spatial.setLocalTranslation(oldPos.add(x,y,z));
    }
}

----


==== More


=== World

First consider the Table a GameLevel. In that specific enviroment, cards are showed, moved rotated…etc..

*Explanation:*
The CardTable take care of 3 things: 

....
  
  (1| Creating:
....

.  Its self from a 16×16 Quad and the Texture
....  The Cards from the OrgCard model with appropriate picture
....  The effects
....  The 3D models

....
  (2| Destroying things in delayed fashion:
....

.  Explode the Card (cool effect!)

....
  (3| Calculate the postions,directions of all action triggered by GamePlay. Arrange and reArrange the cards in hands, in Deck. handPos,gravePos,groundPos, deckPos…etc
....

We need the world to calculate postions the card place and for the translating: the target position of which the card will head to. Remember that we made a Table texture from a 4×4 Grid. Now use that and do simple math ;O :

This part

*  load the level 
*  create a deck
*  load card models with texture (cover) and attach a CardControl to handle the Movement and highlight

[source,java]
----

package magiccard.gameplay

import com.jme3.asset.AssetManager
import com.jme3.material.Material
import com.jme3.math.ColorRGBA
import com.jme3.math.Vector3f
import com.jme3.scene.Node
import com.jme3.scene.Geometry
import com.jme3.scene.Spatial
import com.jme3.scene.shape.Box
import com.jme3.scene.shape.Quad
import sg.atom.gameplay.GameLevel
import sg.atom.stage.GamePlayManager
import sg.atom.stage.WorldManager

import com.jme3.material.RenderState
import com.jme3.renderer.queue.RenderQueue
import com.jme3.scene.shape.Sphere
import com.jme3.math.Quaternion
import com.jme3.math.FastMath
import magiccard.stage.CardSelectControl
import sg.atom.fx.particle.ParticleFactory
import sg.atom.fx.particle.ExplosionNodeControl
import com.jme3.light.PointLight
import com.jme3.effect.ParticleEmitter

class CardTable extends GameLevel{

private Spatial cardOrg;
AssetManager assetManager

// Layout to position cards and table
Vector3f center = new Vector3f(3,0,-5);
float handLength= 2.5f
float spaceBetweenCard = 0.5f
float peakPos= 0.2f
float boardSizeX = 16;
float boardSizeY= 16;

Vector3f scaledCardSize = new Vector3f(1.9f,2.7f,0.02f);
Vector3f gridGapSize = new Vector3f(1,1,1);
public static faceUpQuat = new Quaternion().fromAngles(0f,FastMath.PI,0f);
public static faceDownQuat = new Quaternion().fromAngles(-FastMath.PI,-FastMath.PI,0f);
List actions =[]

public CardTable(GamePlayManager gameplay,WorldManager world,String name,String path){
super(gameplay,world,name,path)
assetManager = world.assetManager
}

Quaternion getCardRot(boolean faceUp){
if (faceUp){
return faceUpQuat.clone()
} else {
return faceDownQuat.clone()
}
}

/*
* Load level, override the method in GameLevel class
*/
public void loadLevel(){
super.loadLevel()
createCardOrg();
createCardBoard();
createEffects();
}

/*
* Short for new Vector3f
*/
Vector3f vec3(float x,float y,float z){
return new Vector3f(x,y,z);
}

/*
* Create the Card board model
*/
public void createCardBoard() {
//levelNode = new Node(“LevelNode”);

Quad cardBoardShape = new Quad(boardSizeX,boardSizeY);
Geometry cardBoard = new Geometry(“CardBoardGeo”, cardBoardShape);

Material mat = new Material(assetManager, “MatDefs/ColoredTextured.j3md”);
mat.setTexture(“ColorMap”, assetManager.loadTexture(“Textures/CardBoard/DiffuseTex.png”));
mat.setColor(“Color”, new ColorRGBA(1, 1, 1, 0.9f));
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
cardBoard.setMaterial(mat);
cardBoard.setLocalTranslation(center.add((float)(-boardSizeX/2),(float)(-boardSizeY/2),0f));
cardBoard.setQueueBucket(RenderQueue.Bucket.Transparent);

levelNode.attachChild(cardBoard);
}
/*
* For debuging positions
*/
public void createSphere(float size,ColorRGBA color,Vector3f pos){
Sphere sh=new Sphere(8,8,size)
Geometry sGeo = new Geometry(“S”,sh)
Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
sGeo.material = mat
mat.setColor(“Color”,color)
sGeo.localTranslation = pos
levelNode.attachChild(sGeo)
}
/*
* Create the orginal Card model
*/
public void createCardOrg(){
cardOrg = ((Node) ((Node) assetManager.loadModel(“Models/Cards/Template/card1.j3o”)).getChild(“Card”)).getChild(“Cube1″);
}

public void createDeck(CardPlayer player) {
// create Cards for player

player.currentDeck.cardList.eachWithIndex{card,i->
//fromDeckToHand(player)
def newCard = createCard(card)
}
arrangeDeck(player)
}

public void createHand(CardPlayer player){
player.hand.eachWithIndex{card,i->
//fromDeckToHand(player)
def newCard = createCard(card)
}
arrangeHand(player)
}

Geometry createCard(Card card) {
// extract the card info
String path = card.picture

// create the geometry
Geometry newCard = (Geometry) cardOrg.clone();
Material cloneMat = newCard.getMaterial().clone();
cloneMat.setTexture(“ColorMap2″, assetManager.loadTexture(path));
newCard.setMaterial(cloneMat);
//cloneMat.setBoolean(“MixGlow”,true);
levelNode.attachChild(newCard);
card.spatial = newCard;
newCard.setLocalScale(-0.4f,-0.4f,0.4f)
// attach the control
newCard.addControl(new CardSpatialControl(worldManager,card));
newCard.addControl(new CardSelectControl(worldManager,gamePlayManager));
return newCard;
}

CardSpatialControl getCardControl(Spatial spatial){
return spatial.getControl(CardSpatialControl.class)
}

----

These pieces help to find the exact location of card in each situation:

*  ArrangeDeck
*  ArrangHand
*  CenterHandPos
*  DeckPos
*  GravePos
*  GroundPos
*  MagicPos

[source,java]
----

public void arrangeDeck(CardPlayer player){
int numOfCards = player.currentDeck.cardList.size()

Vector3f centerDeck = getCenterHandPos(player);
Vector3f gap = vec3(0.4f,0,0.02f);
Vector3f handSize = vec3(0,0,0.5f);

gap = handSize.divide(vec3(1,1,(float) numOfCards));
player.currentDeck.cardList.eachWithIndex{card,i->
//fromDeckToHand(player)
def newCard = card.spatial

//Quaternion rotPIY =new Quaternion().fromAngleAxis(FastMath.PI,Vector3f.UNIT_Y);
//Quaternion rotPIZ =new Quaternion().fromAngleAxis(FastMath.PI,Vector3f.UNIT_Z);
//Quaternion rotXYZ =new Quaternion().fromAngles(0f,FastMath.PI,0f)
//newCard.setLocalRotation(rotXYZ)

Vector3f cardPos = gap.mult(vec3((float)i,1f,(float)i));
Vector3f deckPos;
if (isCurrentPlayer(player)){
deckPos =vec3(9f,-6.5,-5)
} else {
deckPos =vec3(-3f,6.5,-5)
}
newCard.setLocalTranslation(deckPos.add(cardPos))
}
}
public void arrangeHand(CardPlayer player){
int numOfCards = player.hand.size()

Vector3f centerHand = getCenterHandPos(player);
Vector3f gap = vec3(0.4f,0f,0.02f);
float squareSize= boardSizeX-6f
Vector3f handSize = vec3(squareSize,0f,0.02f);

gap = handSize.divide(vec3((float) numOfCards,1,1));
boolean faceUp = isCurrentPlayer(player);
player.hand.eachWithIndex{card,i->
//fromDeckToHand(player)
def newCard = card.spatial

Quaternion rotPIY =new Quaternion().fromAngleAxis(FastMath.PI,Vector3f.UNIT_Y);
Quaternion rotPIZ =new Quaternion().fromAngleAxis(FastMath.PI,Vector3f.UNIT_Z);
Quaternion rotXYZ =getCardRot(faceUp || gamePlayManager.debugMode)
//newCard.setLocalRotation(rotXYZ)

Vector3f cardPos = gap.mult(vec3((float)i,1f,(float)i));
Vector3f handPos = centerHand.add(0f,0f,0f);
//newCard.setLocalTranslation(handPos.add(cardPos));
CardSpatialControl cc=getCardControl(card.spatial)
cc.pos(handPos.add(cardPos))

cc.rot(rotXYZ)

}

}

void putToGrave(){

}
boolean isCurrentPlayer(CardPlayer player){
return gamePlayManager.isCurrentPlayer(player)
}

Vector3f getCenterHandPos(CardPlayer player){
Vector3f centerHandPos;
float halfSizeY = (float)((boardSizeY – 0.3f)/2f);
if (isCurrentPlayer(player)){
centerHandPos = center.add(0f,-halfSizeY,0.5f)
} else {
centerHandPos = center.add(0f,halfSizeY,0.5f)
}
return centerHandPos;
}

Vector3f handPos(CardPlayer player,int index,boolean peak){
Vector3f centerHandPos = getCenterHandPos(player)

int numHandCards =player.hand.size()
float indexf=0

if (index == -1){
numHandCards ++
indexf = numHandCards
} else if (index == -2){
indexf = numHandCards + 2
} else if (index == -3){
indexf = numHandCards
numHandCards = 5
}
horizontalPos = handLength * indexf + numHandCards

Vector3f nextCardPos = vec3(0,horizontalPos,0).add(centerHandPos)

if (peak){
nextCardPos.add(vec3(0,peakPos,0))
}
return nextCardPos;
}

Vector3f deckPos(CardPlayer player){
if (isCurrentPlayer(player)){
return vec3(1,1,1)
} else {
return vec3(1,4,1)
}
}
Vector3f gravePos(CardPlayer player){
if (isCurrentPlayer(player)){
return vec3(1,1,1)
} else {
return vec3(1,4,1)
}
}
Vector3f groundPos(CardPlayer player,int index){
if (isCurrentPlayer(player)){
return vec3(-0.8f,-1.7f,-5).add(scaledCardSize.mult(vec3((float)index,0f,0f)))
} else {
return vec3(-0.8f,1.7f,-5).add(scaledCardSize.mult(vec3((float)index,0f,0f)))
}
}
Vector3f magicPos(CardPlayer player,int index){
if (isCurrentPlayer(player)){
return vec3(-0.8f,-4.5f,-5).add(scaledCardSize.mult(vec3((float)index,0f,0f)))
} else {
return vec3(-0.8f,4.5f,-5).add(scaledCardSize.mult(vec3((float)index,0f,0f)))
}
}
void fromDeckToHand(CardPlayer player,Card card){
Vector3f nextCardPos=handPos(player,-3,false)
CardSpatialControl cc=getCardControl(card.spatial)
cc.pos(nextCardPos)
//cc.faceUp()
//cc.moveTo(nextCardPos,3)
}
void fromHandToMagic(CardPlayer player,Card card){
Vector3f nextCardPos=magicPos(player,player.magic.size())
CardSpatialControl cc=getCardControl(card.spatial)
cc.pos(nextCardPos)
cc.rot(getCardRot(false))

}

void fromHandToGround(CardPlayer player,Card card){
Vector3f nextCardPos=groundPos(player,player.ground.size())
CardSpatialControl cc=getCardControl(card.spatial)
cc.pos(nextCardPos)

}
public Vector3f getCamPos(){
return center.add(0f,-8f,20f)
}


----

This is the update hook of the Level. We update it by clean up and execute delayed actions!!!

[source,java]
----

public void simpleUpdate(float tpf){
// see if anything queued
for (Iterator it=actions.iterator(); it.hasNext(); ) {
def action = it.next();
if ((action.time -= tpf) <0) {
action.do()
it.remove();
} else {
// do nothing but wait
}
}
}

----

Make a cool explosion effect by calling the Atom's ParticleFactory, play it when a card destroyed

[source,java]
----

void destroy(CardPlayer player,Card card){
addExplosion(card.spatial.localTranslation)
CardSpatialControl cc=getCardControl(card.spatial)
cc.gigle = true;
actions<<[
time:1f,
do:{
card.spatial.removeFromParent()
}
]
}

Node explosionPrefab;
void addExplosion(Vector3f pos){
def explosion = explosionPrefab.clone();
explosion.localTranslation =pos.clone();
/*
levelNode.attachChild(flame);
levelNode.attachChild(rain);
levelNode.attachChild(spark);
*/
levelNode.attachChild(explosion);
}

void createEffects(){
ParticleFactory pf = new ParticleFactory(assetManager);
/*
ParticleEmitter flame = pf.createFlame();
flame.setLocalTranslation(new Vector3f(6f,4f,-5f));

ParticleEmitter rain = pf.createRain(“”);
rain.setLocalTranslation(new Vector3f(0,0,-5f));

ParticleEmitter spark = pf.createSpark();
spark.setLocalTranslation(new Vector3f(0,0,-5f));
*/
explosionPrefab = pf.createExplosionNode();
explosionPrefab.getControl(ExplosionNodeControl.class).setMaxTime(2f)
}

----

Load the card character 3d model for a specific card

[source,java]
----

public void loadCharacters(){

Quaternion rot= new Quaternion().fromAngleAxis(FastMath.HALF_PI,Vector3f.UNIT_X)
/*
Spatial demon = assetManager.loadModel(“Models/Demons/BlueEyeWhiteDragon/BlueEyes.j3o”);
demon.scale(0.04f);
demon.setLocalRotation(rot);
levelNode.attachChild(demon);
*/
Spatial witch = assetManager.loadModel(“Models/Demons/Magicians/White/WhiteFemaleMagician.j3o”);
witch.scale(0.5f);
witch.setLocalTranslation(new Vector3f(3f,4f,-5f));
witch.setLocalRotation(rot);
levelNode.attachChild(witch);

Spatial samurai = assetManager.loadModel(“Models/Demons/Warior/SamuraiWarior.j3o”);
samurai.setLocalTranslation(new Vector3f(6f,4f,-5f));
samurai.scale(0.5f);
samurai.setLocalRotation(rot);
levelNode.attachChild(samurai);

/** A white, spot light source. */
PointLight lamp = new PointLight();
lamp.setPosition(getCamPos());
lamp.setColor(ColorRGBA.White);
levelNode.addLight(lamp);
}
}

----


=== Select

For a card game, select is a very important game mechanic, in fact, you are nearly ask to select/choose somethings almost everytime. In Atom, I introduce a generic way to hook your select function into your code game mechanic. 
This section is quite advance for who don’t familiar with groovy but keep your self cheerful cause you going to enjoy Groovy as much as I do! 

Short: 

Detailed: 

For this card game, we will implement a few things in this designed mechanic:


==== Select function:

[source,java]
----



----


==== Select condition:

[source,java]
----



----


==== Select Control:

[source,java]
----

package magiccard.stage;

import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import sg.atom.stage.WorldManager;
import sg.atom.stage.select.SpatialSelectControl;
import magiccard.gameplay.CardGamePlay;
import magiccard.gameplay.Card;
import magiccard.gameplay.CardSpatialControl;
import sg.atom.stage.select.EntitySelectCondition;

/**
*
* @author cuong.nguyenmanh2
*/
public class CardSelectControl extends SpatialSelectControl {

private Vector3f bigScale;
private Vector3f orginalScale;
private Vector3f orginalPos;
private Vector3f peakPos;
private boolean skipOutHover;
private String currentFunction;
private CardGamePlay gameplay;
private Card card;
private boolean isBigger = false;
private boolean isPeak = false;
private boolean isHighlight = false;

public CardSelectControl(WorldManager worldManager, CardGamePlay gameplay) {
currentFunction = “Normal”;
this.gameplay = gameplay;
}

@Override
public void setSpatial(Spatial spatial) {
super.setSpatial(spatial);
this.card = (Card) spatial.getControl(CardSpatialControl.class).getEntity();
orginalScale = spatial.getLocalScale().clone();
bigScale = orginalScale.mult(1.2f);
orginalPos = spatial.getLocalTranslation().clone();
}

@Override
protected void doSelected() {
super.doSelected();
//orginalScale = spatial.getLocalScale();
//orginalPos = spatial.getLocalTranslation().clone();
//peakPos = orginalPos.add(new Vector3f(0f, 0.4f, 0f));
//spatial.setLocalTranslation(peakPos);
//spatial.setLocalScale(bigScale);
}

@Override
protected void doDeselected() {
super.doDeselected();
//spatial.setLocalScale(orginalScale);
//spatial.setLocalTranslation(orginalPos.clone());

}

@Override
protected void doHovered() {
if (this.isHoverable()) {
super.doHovered();
//orginalScale = spatial.getLocalScale();
EntitySelectCondition inHand = (EntitySelectCondition) gameplay.cardSelectRules.get(“inHand”);
EntitySelectCondition inGround = (EntitySelectCondition) gameplay.cardSelectRules.get(“inGround”);
if (inHand.isSelected(card)) {
peak();
bigger();
highlight();
} else if (inGround.isSelected(card)) {
// The card is the ground
highlight();
}
}
}

@Override
protected void doOutHovered() {
if (this.isHoverable()) {
if (!this.skipOutHover) {
super.doOutHovered();

noPeak();
smaller();
noHighlight();

}
}
}

public String getCurrentFunction() {
return currentFunction;
}

public void setCurrentFunction(String currentFunction) {
this.currentFunction = currentFunction;
}

public void setSkipOutHover(boolean skipOutHover) {
this.skipOutHover = skipOutHover;
}

public boolean isSkipOutHover() {
return skipOutHover;
}

void bigger() {
if (!isBigger) {
spatial.setLocalScale(bigScale);
isBigger = true;
}
}

void peak() {
if (!isPeak) {
orginalPos = spatial.getLocalTranslation().clone();
peakPos = orginalPos.add(new Vector3f(0f, 0.2f, 0.2f));
spatial.setLocalTranslation(peakPos);
isPeak = true;
}
}

void noPeak() {
if (isPeak) {
spatial.setLocalTranslation(orginalPos.clone());
isPeak = false;
}
}

void smaller() {
if (isBigger) {
spatial.setLocalScale(orginalScale);
isBigger = false;
}
}

void highlight() {
if (!isHighlight) {
((Geometry) spatial).getMaterial().setBoolean(“MixGlow”, true);
isHighlight = true;
}
}

void noHighlight() {
if (isHighlight) {
((Geometry) spatial).getMaterial().setBoolean(“MixGlow”, false);
isHighlight = false;
}
}
}

----


==== Cached conditions – Rules:

We use a Groovy Map cardSelectRules to save all rule in form of Closure that determine if a Card is selected or not. More about rules in the scripting part <<jme3/atomixtuts/cardsgame/scripting#,scripting>>

…part of CardGamePlay.groovy

[source,java]
----

public Map cardSelectRules=[:];
void createSelectConditions(){
// RULE : The card is in hand
def inHandRule ={card->

CardPlayer player = whichPlayerCard(card)
if (player.getHand().contains(card)) {
return true;
}
return false;
}
cardSelectRules["inHand"] =new ClosureSelectCondition(inHandRule);

// RULE : The card is in hand of the current player – who play this device!
def inHandCurrentRule ={card->
if (isCurrentPlayer(whichPlayerCard(card))) {
CardPlayer player = getCurrentPlayer();
if (player.getHand().contains(card)) {
return true;
}
}
return false;
}
cardSelectRules["inHandCurrent"] =new ClosureSelectCondition(inHandCurrentRule);

// RULE : The card is in hand of the current turn player – who has this turn!
def inHandTurnRule ={card->
if (isCurrentPlayer(whichPlayerCard(card))) {
CardPlayer player = getCurrentPlayer();
if (player.getHand().contains(card)) {
return true;
}
}
return false;
}
cardSelectRules["inHandTurn"] =new ClosureSelectCondition(inHandTurnRule);
// RULE: The card is in ground
def inGroundRule ={card->

CardPlayer player = whichPlayerCard(card);
if (player.ground.contains(card)) {
return true;
}
return false;
}
cardSelectRules["inGround"] =new ClosureSelectCondition(inGroundRule);
// RULE: Card in player Main phase
def mainPhaseCards = {card->
if (isCurrentPlayer(whichPlayerCard(card))) {
CardPlayer player = getCurrentPlayer();
if (player.hand.contains(card)||player.ground.contains(card)||player.magic.contains(card)) {
//println(“Condition true”);
return true;
}
}
return false;
}
cardSelectRules["mainPhaseCards"] =new ClosureSelectCondition(mainPhaseCards);
// RULE: Can not select any thing
def noneSelect = {card->
return false;
}
cardSelectRules["noneSelect"] =new ClosureSelectCondition(noneSelect);

}

----


=== States


==== MainMenuState


==== InGameState


==== LoadingState


=== Managers


=== Controls


=== CardLibrary
