PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/MWServer/src/main/java/mw/server/model/Card.java

http://magicwars.googlecode.com/
Java | 1670 lines | 1173 code | 309 blank | 188 comment | 154 complexity | 37f77c670f1247bf626eac6f8bd2b227 MD5 | raw file
  1. package mw.server.model;
  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.Map.Entry;
  8. import mw.mtgforge.Constant;
  9. import mw.server.GameManager;
  10. import mw.server.card.GroovyScriptFacade;
  11. import mw.server.event.EventManager;
  12. import mw.server.model.MagicWarsModel.CardSuperType;
  13. import mw.server.model.MagicWarsModel.CardType;
  14. import mw.server.model.MagicWarsModel.Color;
  15. import mw.server.model.MagicWarsModel.GameZone;
  16. import mw.server.model.ability.ManaAbility;
  17. import mw.server.model.cost.ManaCost;
  18. import mw.server.pattern.Command;
  19. import mw.server.pattern.CommandArgs;
  20. import mw.server.pattern.CommandChain;
  21. import mw.server.pattern.CommandChain.ExecuteOrder;
  22. import mw.utils.CommonUtil;
  23. import org.apache.log4j.Logger;
  24. /**
  25. * @author mtgforge modified by nantuko
  26. */
  27. public class Card implements Serializable, Cloneable {
  28. private static final Logger log = Logger.getLogger(Card.class);
  29. private static int nextUniqueNumber;
  30. private int uniqueNumber = nextUniqueNumber++;
  31. private boolean isScripted = false;
  32. private GameManager game;
  33. private ArrayList<String> keyword = new ArrayList<String>();
  34. private Map<String, List<Object>> aspects = new HashMap<String, List<Object>>();
  35. private ArrayList<Card> attached = new ArrayList<Card>();
  36. private List<CardSuperType> cardSuperType = new ArrayList<CardSuperType>();
  37. private List<CardType> cardType = new ArrayList<CardType>();
  38. private List<String> cardSubType = new ArrayList<String>();
  39. /**
  40. * Spell abilities.
  41. */
  42. private ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>();
  43. /**
  44. * Spell color
  45. */
  46. private ArrayList<String> color = new ArrayList<String>();
  47. private boolean tapped;
  48. //private EditableProperty<Boolean> tapped;
  49. private boolean sickness = true;// summoning sickness
  50. private boolean token = false;
  51. private boolean suspended = false;
  52. private boolean isAttacking = false;
  53. private boolean isBlocking = false;
  54. /**
  55. * When figuring a creature's power and toughness, apply effects in a series of sublayers in the following order:
  56. * (a) effects from characteristic-defining abilities;
  57. * (b) effects that set power and/or toughness to a specific number or value;
  58. * (c) effects that modify power and/or toughness but don't set power and/or toughness to a specific number or value;
  59. * (d) changes from counters;
  60. * (e) effects that switch a creature's power and toughness.
  61. * This means that effects that switch power and toughness are always applied last
  62. * when determining a creature's power and toughness,
  63. * even if other power/toughness-changing effects are created later in the turn.
  64. */
  65. private int power;
  66. private int toughness;
  67. private int pumpAttack;
  68. private int pumpDefense;
  69. private boolean powerToughnessIsSwitched;
  70. /**
  71. * This values can't be changed. In real life this is what is displayed on printed card.
  72. */
  73. private final int basePower;
  74. private final int baseToughness;
  75. private final List<String> baseColor;
  76. private int damage;
  77. private Damage assignedDamage = new Damage();
  78. private int nShield;
  79. private int turnInZone;
  80. private int abilityLastPlayedTurn = 0;
  81. private boolean canBePrevented = true;
  82. /**
  83. * Mana abilities
  84. */
  85. ArrayList<String> manaAbilities = new ArrayList<String>();
  86. /**
  87. * Table ID.
  88. */
  89. private int tableID;
  90. /**
  91. * Owner ID.
  92. */
  93. private int ownerID = 0;
  94. /**
  95. * Controller ID.
  96. */
  97. private int controllerID = 0;
  98. private String name = "";
  99. private String rarity = "";
  100. private String text = "";
  101. private ArrayList<String> oracleText = new ArrayList<String>();
  102. private String manaCost = "";
  103. private String setName = "";
  104. private int collectorID = 0;
  105. private boolean hybrid = false;
  106. private CommandChain entersTheGame = new CommandChain(Command.Blank, null);
  107. private CommandChain entersTheBattlefield = new CommandChain(Command.Blank, null);
  108. private CommandChain leavesTheBattlefield = new CommandChain(Command.Blank, null);
  109. private Command destroyCommand = Command.Blank;
  110. private CommandChain dealDamageCommand = new CommandChain(Command.Blank, null);
  111. //private Command dealDamageCommand = Command.Blank;
  112. private Command dealDamageToCreatureCommand = Command.Blank;
  113. private Command dealtDamageCommand = null;
  114. private Command dealtDamageCommandEx = null;
  115. private Command whenPlayCommand = Command.Blank;
  116. private CommandArgs changeCountersCommand = CommandArgs.Blank;
  117. private Command equipCommand = Command.Blank;
  118. private Command unEquipCommand = Command.Blank;
  119. private Command cyclingCommand = Command.Blank;
  120. private Command changeControllerCommand = null;
  121. private Command asLongAsCommand = null;
  122. private Command preCombatMainPhaseCommand = null;
  123. private int latestDealtDamage;
  124. private boolean isPlayer;
  125. private int playerIdToAsk = -1;
  126. private boolean flashback = false;
  127. /**
  128. * Counters that prevent the permanent to being tapped.
  129. */
  130. private int preventUntapCounters;
  131. /**
  132. * Amount of damage that should be prevented
  133. */
  134. private int preventDamage;
  135. /**
  136. * Contains cards this card is equipped by
  137. */
  138. private ArrayList<Card> equippedBy = new ArrayList<Card>();
  139. /**
  140. * Contain card that this card equips
  141. */
  142. private ArrayList<Card> equipping = new ArrayList<Card>(); //equipping size will always be 0 or 1
  143. /**
  144. * Contains counters of different types.
  145. */
  146. private HashMap<CounterType,Integer> counters = new HashMap<CounterType,Integer>();
  147. /**
  148. * Reference to groovy facade object.
  149. * Used by groovy scripting to get access to compiled groovy class.
  150. */
  151. private GroovyScriptFacade groovyRef;
  152. /**
  153. * Default constructor
  154. */
  155. public Card() {
  156. this.basePower = this.baseToughness = 0;
  157. this.baseColor = new ArrayList<String>();
  158. this.baseColor.add(Constant.Color.Colorless);
  159. color.add(Constant.Color.Colorless);
  160. }
  161. public Card(int basePower, int baseToughness, List<String> baseColor) {
  162. this.basePower = basePower;
  163. this.baseToughness = baseToughness;
  164. this.power = basePower;
  165. this.toughness = baseToughness;
  166. this.baseColor = baseColor;
  167. for (String _color : baseColor) color.add(_color);
  168. }
  169. public void addLoyaltyCounter(int n) {
  170. putCounter(CounterType.LOYALTY, n);
  171. }
  172. public void subtractLoyaltyCounter(int n) {
  173. removeCounter(CounterType.LOYALTY, n);
  174. }
  175. public int getLoyaltyCounters() {
  176. return getCounters(CounterType.LOYALTY);
  177. }
  178. // the amount of damage needed to kill the creature
  179. public int getKillDamage() {
  180. return getDefense() - getDamage();
  181. }
  182. public int getTurnInZone() {
  183. return turnInZone;
  184. }
  185. public void setTurnInZone(int turn) {
  186. turnInZone = turn;
  187. }
  188. public void setManaCost(String s) {
  189. manaCost = s;
  190. }
  191. public String getManaCost() {
  192. return manaCost;
  193. }
  194. public ManaCost getManacost() {
  195. return new ManaCost(this.manaCost);
  196. }
  197. public String getSpellText() {
  198. return text;
  199. }
  200. public void setText(String t) {
  201. text = t;
  202. }
  203. public String getText() {
  204. String t = text.replace("{this}", this.name);
  205. return t;
  206. }
  207. public boolean isAttacking() {
  208. return isAttacking;
  209. }
  210. public void setAttacking(boolean isAttacking) {
  211. this.isAttacking = isAttacking;
  212. }
  213. public String getDetailedText() {
  214. if (isInstant() || isSorcery()) {
  215. String s = getSpellText();
  216. SpellAbility[] sa = getSpellAbility();
  217. for (int i = 0; i < sa.length; i++)
  218. s += sa[i].toString();
  219. return s;
  220. }
  221. String s = "";
  222. ArrayList<String> keyword = getKeyword();
  223. for (int i = 0; i < keyword.size(); i++) {
  224. if (i != 0)
  225. s += ", ";
  226. s += keyword.get(i).toString();
  227. }
  228. s += "\r\n" + text;
  229. SpellAbility[] sa = getSpellAbility();
  230. for (int i = 0; i < sa.length; i++) {
  231. // presumes the first SpellAbility added to this card, is the "main"
  232. // spell
  233. // skip the first SpellAbility for creatures, since it says
  234. // "Summon this creature"
  235. // looks bad on the Gui card detail
  236. if (isPermanent() && i != 0) {
  237. s += sa[i].toString();
  238. }
  239. }
  240. return s.trim();
  241. }// getText()
  242. public void clearSpellAbility() {
  243. spellAbility.clear();
  244. }
  245. public void addSpellAbility(SpellAbility a) {
  246. spellAbility.add(a);
  247. }
  248. public SpellAbility[] getSpellAbility() {
  249. ArrayList<SpellAbility> spells = new ArrayList<SpellAbility>();
  250. for (SpellAbility spell : spellAbility) {
  251. /*f (spell instanceof SpellChainFactory) {
  252. spells.add(((SpellChainFactory) spell).getMainSpell());
  253. } else {
  254. spells.add(spell);
  255. }*/
  256. spells.add(spell);
  257. }
  258. SpellAbility[] s = new SpellAbility[spells.size()];
  259. spells.toArray(s);
  260. return s;
  261. }
  262. public ArrayList<SpellAbility> getSpellAbilities() {
  263. return spellAbility;
  264. }
  265. // shield = regeneration
  266. public void setShield(int n) {
  267. nShield = n;
  268. }
  269. public int getShield() {
  270. return nShield;
  271. }
  272. public void addShield() {
  273. nShield++;
  274. }
  275. public void subtractShield() {
  276. nShield--;
  277. }
  278. public void resetShield() {
  279. nShield = 0;
  280. }
  281. // is this "Card" supposed to be a token?
  282. public void setToken(boolean b) {
  283. token = b;
  284. }
  285. public boolean isToken() {
  286. return token;
  287. }
  288. public void setEntersTheGameCommand(Command entersTheGameCommand) {
  289. entersTheGame = new CommandChain(entersTheGameCommand, entersTheGame);
  290. }
  291. /**
  292. * This method calls commands to prepare card for the game.
  293. * At least it is used to register observers whenever card appears in the game.
  294. * Note: card enters the game only once (usually when library is built) and never leaves.
  295. */
  296. public void entersTheGame() {
  297. entersTheGame.executeChain();
  298. }
  299. /**
  300. * Adds EntersTheBattlefield Trigger Command
  301. * The command will be executed first,
  302. * use setEntersTheBattlefieldCommand(Command entersTheBattlefieldCommand, boolean backOrder)
  303. * if you want to execute it at the end
  304. *
  305. * @param entersTheBattlefieldCommand
  306. */
  307. public void setEntersTheBattlefieldCommand(Command entersTheBattlefieldCommand) {
  308. entersTheBattlefield = new CommandChain(entersTheBattlefieldCommand, entersTheBattlefield);
  309. }
  310. /**
  311. * Adds EntersTheBattlefield Trigger Command
  312. * Allows to add command at the end (backOrder = true)
  313. *
  314. * @param entersTheBattlefieldCommand
  315. * @param order a
  316. */
  317. public void setEntersTheBattlefieldCommand(Command entersTheBattlefieldCommand, ExecuteOrder order) {
  318. entersTheBattlefield = new CommandChain(entersTheBattlefieldCommand, entersTheBattlefield, order);
  319. }
  320. public void entersTheBattlefield() {
  321. entersTheBattlefield.executeChain();
  322. }
  323. public Command getChangeControllerCommand() {
  324. return changeControllerCommand;
  325. }
  326. public void setChangeControllerCommand(Command changeControllerCommand) {
  327. this.changeControllerCommand = changeControllerCommand;
  328. }
  329. public boolean hasComesIntoPlayCommand() {
  330. return !entersTheBattlefield.isBlank();
  331. }
  332. public void setDestroyCommand(Command c) {
  333. destroyCommand = c;
  334. }
  335. public void destroy() {
  336. destroyCommand.execute();
  337. }
  338. public void leavesTheBattlefield() {
  339. leavesTheBattlefield.executeChain();
  340. }
  341. public void setLeavesTheBattlefieldCommand(Command leavesTheBattlefieldCommand) {
  342. leavesTheBattlefield = new CommandChain(leavesTheBattlefieldCommand, leavesTheBattlefield);
  343. }
  344. public Command getCyclingCommand() {
  345. return cyclingCommand;
  346. }
  347. public void setCyclingCommand(Command cyclingCommand) {
  348. this.cyclingCommand = cyclingCommand;
  349. }
  350. public Command getPreCombatMainPhaseCommand() {
  351. return preCombatMainPhaseCommand;
  352. }
  353. public void setPreCombatMainPhaseCommand(Command preCombatMainPhaseCommand) {
  354. this.preCombatMainPhaseCommand = preCombatMainPhaseCommand;
  355. }
  356. public void setSickness(boolean b) {
  357. sickness = b;
  358. }
  359. public boolean hasSickness() {
  360. if (getKeyword().contains(MagicWarsModel.KEYWORD_HASTE_SA))
  361. return false;
  362. return sickness;
  363. }
  364. public boolean hasFlying() {
  365. return getKeyword().contains("Flying");
  366. }
  367. public void setRarity(String s) {
  368. rarity = s;
  369. }
  370. public String getRarity() {
  371. return rarity;
  372. }
  373. /**
  374. * Use GameManager.addDamage(int tableID, int damage) instead. The method
  375. * has default access that used by CardProxy.
  376. */
  377. void addDamage(int n, Card source) {
  378. latestDealtDamage = n;
  379. if (dealtDamageCommand != null) {
  380. dealtDamageCommand.execute();
  381. }
  382. if (dealtDamageCommandEx != null) {
  383. dealtDamageCommandEx.setTarget(source);
  384. dealtDamageCommandEx.execute();
  385. }
  386. setDamage(getDamage() + latestDealtDamage);
  387. }
  388. void setDamage(int n) {
  389. damage = n;
  390. }
  391. public int getDamage() {
  392. return damage;
  393. }
  394. public void setAssignedDamage(Damage d) {
  395. assignedDamage = d;
  396. }
  397. public Damage getAssignedDamage() {
  398. return assignedDamage;
  399. }
  400. public String getName() {
  401. return name;
  402. }
  403. public boolean name(String name) {
  404. return this.name.equals(name);
  405. }
  406. public int getOwnerID() {
  407. return ownerID;
  408. }
  409. public int getOwner() {
  410. return getOwnerID();
  411. }
  412. public int getControllerID() {
  413. return controllerID;
  414. }
  415. public int getController() {
  416. return getControllerID();
  417. }
  418. public void setName(String s) {
  419. name = s;
  420. }
  421. public String getCardKey() {
  422. return setName + ':' + collectorID;
  423. }
  424. public void setOwner(int ownerID) {
  425. this.ownerID = ownerID;
  426. }
  427. public void setController(int controllerID) {
  428. this.controllerID = controllerID;
  429. // this.updateObservers();
  430. }
  431. // array size might equal 0, will NEVER be null
  432. public ArrayList<Card> getAttachedCards() {
  433. return attached;
  434. }
  435. public boolean hasAttachedCards() {
  436. return attached.size() != 0;
  437. }
  438. public void attachCard(Card c) {
  439. attached.add(c);
  440. }
  441. public void unattachCard(Card c) {
  442. attached.remove(c);
  443. }
  444. public void setCardSubType(List<String> subTypes) {
  445. this.cardSubType = new ArrayList<String>(subTypes);
  446. }
  447. public void setCardType(List<CardType> cardTypes) {
  448. this.cardType = new ArrayList<CardType>(cardTypes);
  449. }
  450. public void setCardSuperType(List<CardSuperType> superTypes) {
  451. this.cardSuperType = new ArrayList<CardSuperType>(superTypes);
  452. }
  453. public void addType(String type) {
  454. CommonUtil.addType(this, type);
  455. }
  456. public void addSubType(String subType) {
  457. cardSubType.add(subType);
  458. }
  459. public void addCardType(CardType type) {
  460. cardType.add(type);
  461. }
  462. public void addSuperType(CardSuperType superType) {
  463. cardSuperType.add(superType);
  464. }
  465. public void removeType(String a) {
  466. cardSubType.remove(a);
  467. }
  468. public void resetType() {
  469. cardSubType.clear();
  470. }
  471. private boolean typeContains(List<?> list, Object s) {
  472. if (s == null) return false;
  473. return list.contains(s);
  474. }
  475. public boolean type(String type) {
  476. return subType(type) || cardType(CardType.getByName(type)) || superType(CardSuperType.getByName(type));
  477. }
  478. /**
  479. * The same as type(String type)
  480. * @param subType
  481. * @return
  482. */
  483. public boolean subType(String subType) {
  484. return typeContains(this.cardSubType, subType);
  485. }
  486. public boolean cardType(CardType cardType) {
  487. return typeContains(this.cardType, cardType);
  488. }
  489. public boolean superType(CardSuperType superType) {
  490. return typeContains(this.cardSuperType, superType);
  491. }
  492. public ArrayList<String> getType() {
  493. return new ArrayList<String>(this.cardSubType);
  494. }
  495. public ArrayList<String> getSubType() {
  496. return getType();
  497. }
  498. public ArrayList<CardType> getCardType() {
  499. return new ArrayList<CardType>(this.cardType);
  500. }
  501. public ArrayList<CardSuperType> getCardSuperType() {
  502. return new ArrayList<CardSuperType>(this.cardSuperType);
  503. }
  504. public int getAttack() {
  505. if (!powerToughnessIsSwitched) {
  506. return power + pumpAttack + getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1);
  507. } else {
  508. return toughness + pumpDefense + getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1);
  509. }
  510. }
  511. public int getPower() {
  512. return getAttack();
  513. }
  514. public int getDefense() {
  515. if (!powerToughnessIsSwitched) {
  516. return toughness + pumpDefense + getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1);
  517. } else {
  518. return power + pumpAttack + getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1);
  519. }
  520. }
  521. public int getToughness() {
  522. return getDefense();
  523. }
  524. public int getBaseAttack() {
  525. return power;
  526. }
  527. public int getBaseDefense() {
  528. return toughness;
  529. }
  530. public void switchPowerToughness() {
  531. powerToughnessIsSwitched = !powerToughnessIsSwitched;
  532. }
  533. public void setAttack(int n) {
  534. power = n;
  535. }
  536. public void addAttack(int n) {
  537. pumpAttack += n;
  538. }
  539. public void subAttack(int n) {
  540. pumpAttack -= n;
  541. }
  542. public void addDefense(int n) {
  543. pumpDefense += n;
  544. }
  545. public void subDefense(int n) {
  546. pumpDefense -= n;
  547. }
  548. public void setDefense(int n) {
  549. toughness = n;
  550. }
  551. public void resetPump() {
  552. pumpAttack = 0;
  553. pumpDefense = 0;
  554. }
  555. /**
  556. * Set power and toughness to original.
  557. */
  558. public void resetPT() {
  559. power = basePower;
  560. toughness = baseToughness;
  561. }
  562. public boolean isUntapped() {
  563. return !tapped;
  564. //return !tapped.getValue();
  565. }
  566. public boolean isTapped() {
  567. return tapped;
  568. //return tapped.getValue();
  569. }
  570. private void setTapped(boolean value) {
  571. tapped = value;
  572. //tapped.setValue(value);
  573. }
  574. public void entersTheBattlefieldTapped() {
  575. tapped = true;
  576. //tapped.setValueSilent(true);
  577. }
  578. public void tap() {
  579. setTapped(true);
  580. if (this.game == null) {
  581. throw new RuntimeException("Game reference is null.");
  582. }
  583. getGame().getEventManager().getTapEvent().notifyObservers(this);
  584. }
  585. public void untap() {
  586. if (preventUntapCounters == 0) {
  587. setTapped(false);
  588. }
  589. }
  590. /**
  591. * Keywords are like flying, fear, first strike, etc...
  592. * @return
  593. */
  594. public ArrayList<String> getKeyword() {
  595. return new ArrayList<String>(keyword);
  596. }
  597. public void setKeyword(ArrayList<String> a) {
  598. keyword = new ArrayList<String>(a);
  599. }
  600. public void addKeyword(String s) {
  601. keyword.add(s);
  602. }
  603. public void removeKeyword(String s) {
  604. keyword.remove(s);
  605. }
  606. public boolean hasKeyword(String s) {
  607. return keyword.contains(s);
  608. }
  609. public boolean keyword(String s) {
  610. return hasKeyword(s);
  611. }
  612. /**
  613. * Aspects are to save card specific information
  614. * Example: touchedByGloryofWarfare that means that creature has been already pumped by the card
  615. * @return
  616. */
  617. public Map<String, List<Object>> getAspects() {
  618. return new HashMap<String, List<Object>>(aspects);
  619. }
  620. public Object getAspectValue(String aspect) {
  621. if (aspects.containsKey(aspect)) {
  622. List<Object> values = aspects.get(aspect);
  623. if (values != null) {
  624. return values.get(values.size()-1);
  625. }
  626. }
  627. return "";
  628. }
  629. public List<Object> getAspectValues(String aspect) {
  630. if (aspects.containsKey(aspect)) {
  631. List<Object> values = aspects.get(aspect);
  632. if (values != null) {
  633. return values;
  634. }
  635. }
  636. return new ArrayList<Object>();
  637. }
  638. public void setAspects(Map<String, List<Object>> a) {
  639. aspects = a;
  640. }
  641. public void addAspect(String aspect) {
  642. aspects.put(aspect, null);
  643. }
  644. public void addAspect(String aspect, Object value) {
  645. if (aspects.containsKey(aspect)) {
  646. List<Object> values = aspects.get(aspect);
  647. if (values == null) {
  648. values = new ArrayList<Object>();
  649. }
  650. values.add(value);
  651. aspects.put(aspect, values);
  652. } else {
  653. List<Object> values = new ArrayList<Object>();
  654. values.add(value);
  655. aspects.put(aspect, values);
  656. }
  657. }
  658. public void removeAspect(String aspect) {
  659. if (aspects.containsKey(aspect)) {
  660. List<Object> values = aspects.get(aspect);
  661. if (values == null || values.size() < 2) {
  662. aspects.remove(aspect);
  663. } else {
  664. values.remove(values.size() - 1);
  665. aspects.put(aspect, values);
  666. }
  667. }
  668. }
  669. public boolean hasAspect(String s) {
  670. return aspects.containsKey(s);
  671. }
  672. public void updateAspectValue(String aspect, Serializable value) {
  673. if (aspects.containsKey(aspect)) {
  674. List<Object> values = aspects.get(aspect);
  675. if (values != null && values.size() > 0) {
  676. values.remove(values.size()-1);
  677. }
  678. values.add(value);
  679. } else {
  680. addAspect(aspect, value);
  681. }
  682. }
  683. ///////////////////////////////////////////////////////////////////////////////////////////////
  684. public boolean isPermanent() {
  685. return !(isInstant() || isSorcery());
  686. }
  687. public boolean isCreature() {
  688. return cardType(CardType.Creature);
  689. }
  690. public boolean isBasicLand() {
  691. return superType(CardSuperType.Basic) && isLand();
  692. }
  693. public boolean isLand() {
  694. return cardType(CardType.Land);
  695. }
  696. public boolean isSorcery() {
  697. return cardType(CardType.Sorcery);
  698. }
  699. public boolean isInstant() {
  700. return cardType(CardType.Instant);
  701. }
  702. public boolean isArtifact() {
  703. return cardType(CardType.Artifact);
  704. }
  705. public boolean isEquipment() {
  706. return subType("Equipment");
  707. }
  708. public boolean isPlaneswalker() {
  709. return cardType(CardType.Planeswalker);
  710. }
  711. // global and local enchantments
  712. public boolean isEnchantment() {
  713. return cardType(CardType.Enchantment);
  714. }
  715. public boolean isAura() {
  716. return subType("Aura");
  717. }
  718. public void setUniqueNumber(int n) {
  719. uniqueNumber = n;
  720. }
  721. public int getUniqueNumber() {
  722. return uniqueNumber;
  723. }
  724. public ArrayList<String> getColor() {
  725. return color;
  726. }
  727. public boolean isColored() {
  728. return color.contains(Color.White.toCode()) || color.contains(Color.Green.toCode()) || color.contains(Color.Red.toCode()) || color.contains(Color.Black.toCode())
  729. || color.contains(Color.Blue.toCode());
  730. }
  731. public boolean isMulticolored() {
  732. int count = 0;
  733. if (color.contains(Color.White.toCode())) count++;
  734. if (color.contains(Color.Green.toCode())) count++;
  735. if (color.contains(Color.Red.toCode())) count++;
  736. if (color.contains(Color.Black.toCode())) count++;
  737. if (color.contains(Color.Blue.toCode())) count++;
  738. return count > 1;
  739. }
  740. public void resetColor() {
  741. this.color.clear();
  742. for (String _color : baseColor) {
  743. this.color.add(_color);
  744. }
  745. }
  746. public void setColor(String color) {
  747. this.color.clear();
  748. this.color.add(color);
  749. }
  750. public void setColors(ArrayList<String> colors) {
  751. this.color = colors;
  752. }
  753. public void addColor(String color) {
  754. this.color.add(color);
  755. }
  756. public void removeColor(String color) {
  757. this.color.remove(color);
  758. }
  759. public void addColor(MagicWarsModel.Color color) {
  760. addColor(color.toCode());
  761. }
  762. public void removeColor(MagicWarsModel.Color color) {
  763. removeColor(color.toCode());
  764. }
  765. public void setColor(MagicWarsModel.Color color) {
  766. setColor(color.toCode());
  767. }
  768. public boolean color(Color color) {
  769. return this.color.contains(color.toCode());
  770. }
  771. public int getTableID() {
  772. return tableID;
  773. }
  774. public void setTableID(int id) {
  775. tableID = id;
  776. }
  777. public void dealCombatDamageToPlayer() {
  778. dealDamageCommand.executeChain();
  779. }
  780. public void setDamagePlayerCommand(Command c) {
  781. dealDamageCommand = new CommandChain(c, dealDamageCommand);
  782. }
  783. public void dealDamageToCreatureCommand() {
  784. dealDamageToCreatureCommand.execute();
  785. }
  786. public void setDealDamageToCreatureCommand(Command c) {
  787. dealDamageToCreatureCommand = c;
  788. }
  789. public void setDealtDamageCommand(Command c) {
  790. dealtDamageCommand = c;
  791. }
  792. public Command getDealtDamageCommand() {
  793. return dealtDamageCommand;
  794. }
  795. public void setDealtDamageCommandEx(Command c) {
  796. dealtDamageCommandEx = c;
  797. }
  798. public Command getDealtDamageCommandEx() {
  799. return dealtDamageCommand;
  800. }
  801. public boolean equals(Object o) {
  802. if (o instanceof Card) {
  803. Card c = (Card) o;
  804. int a = getUniqueNumber();
  805. int b = c.getUniqueNumber();
  806. //int a1 = getTableID();
  807. //int b1 = c.getTableID();
  808. //return (a == b && a1 == b1);
  809. return a == b;
  810. }
  811. return false;
  812. }
  813. /**
  814. * Compare card parameters (type, controller, keywords, etc.)
  815. * @param card
  816. * @return true if the same
  817. */
  818. public boolean equalsTo(Card card) {
  819. List<CardType> newCardType = card.getCardType();
  820. List<CardSuperType> newCardSuperType = card.getCardSuperType();
  821. List<String> newCardSubType = card.getType();
  822. List<String> newCardKeyword = card.getKeyword();
  823. List<String> newCardColor = card.getColor();
  824. HashMap<CounterType, Integer> newCardCounters = card.getAllCounters();
  825. ArrayList<String> newCardMA = card.getManaAbilities();
  826. ArrayList<Card> newCardAttached = card.getAttachedCards();
  827. // quick check
  828. if (cardType.size() != newCardType.size()) return false;
  829. if (keyword.size() != newCardKeyword.size()) return false;
  830. if (color.size() != newCardColor.size()) return false;
  831. if (counters.size() != newCardCounters.size()) return false;
  832. if (manaAbilities.size() != newCardMA.size()) return false;
  833. if (attached.size() != newCardAttached.size()) return false;
  834. if (isAttacking != card.isAttacking) return false;
  835. if (isBlocking != card.isBlocking) return false;
  836. //if (tapped.getValue() != card.isTapped()) return false;
  837. if (tapped != card.isTapped()) return false;
  838. if (hasSickness() != card.hasSickness()) return false;
  839. if (isEquipping() != card.isEquipping()) return false;
  840. if (damage != card.getDamage()) return false;
  841. // name
  842. if (!getName().equals(card.getName())) return false;
  843. // controller
  844. if (getController() != card.getController()) return false;
  845. // type
  846. for (CardSuperType t : cardSuperType) {
  847. if (!newCardSuperType.contains(t)) return false;
  848. }
  849. for (CardType t : cardType) {
  850. if (!newCardType.contains(t)) return false;
  851. }
  852. for (String t : cardSubType) {
  853. if (!newCardSubType.contains(t)) return false;
  854. }
  855. // power/toughness
  856. if (isCreature()) {
  857. if (getAttack() != card.getAttack()) return false;
  858. if (getDefense() != card.getDefense()) return false;
  859. }
  860. // keyword
  861. for (String k : keyword) {
  862. if (!newCardKeyword.contains(k)) return false;
  863. }
  864. // color
  865. for (String c : color) {
  866. if (!newCardColor.contains(c)) return false;
  867. }
  868. // counters
  869. for (CounterType ct : counters.keySet()) {
  870. if (!newCardCounters.containsKey(ct)) return false;
  871. if (!counters.get(ct).equals(newCardCounters.get(ct))) return false;
  872. }
  873. // mana abilities
  874. for (String m : manaAbilities) {
  875. if (!newCardMA.contains(m)) return false;
  876. }
  877. return true;
  878. }
  879. public int hashCode() {
  880. return getUniqueNumber();
  881. }
  882. public String toString() {
  883. String lc = "";
  884. String tid = "";
  885. Integer count = getLoyaltyCounters();
  886. if (count > 0) {
  887. lc = "[" + count + "]";
  888. }
  889. if (this.getTableID() > 0) {
  890. tid = " (" + this.getTableID() + ")";
  891. }
  892. return this.getName() + lc + tid;
  893. }
  894. public String toStringWithUID() {
  895. return this.getName() + "(" + this.getUniqueNumber() + ")";
  896. }
  897. public int getLatestDealtDamage() {
  898. return latestDealtDamage;
  899. }
  900. public void setLatestDealtDamage(int latestDealtDamage) {
  901. this.latestDealtDamage = latestDealtDamage;
  902. }
  903. public int getAbilityLastPlayedTurn() {
  904. return abilityLastPlayedTurn;
  905. }
  906. public void setAbilityLastPlayedTurn(int turn) {
  907. this.abilityLastPlayedTurn = turn;
  908. }
  909. public int getPreventUntapCounters() {
  910. return preventUntapCounters;
  911. }
  912. public void setPreventUntapCounters(int preventUntapCounters) {
  913. this.preventUntapCounters = preventUntapCounters;
  914. }
  915. public void setCounters(CounterType counterType, int n) {
  916. counters.put(counterType, Integer.valueOf(n));
  917. if (counterType.equals(CounterType.P1P1) || counterType.equals(CounterType.M1M1)) {
  918. checkPlusAndMinusCounters();
  919. changeCountersCommand.execute(this, getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1));
  920. }
  921. }
  922. public void putCounter(CounterType counterType, int n) {
  923. if (counters.containsKey(counterType)) {
  924. Integer count = counters.get(counterType) + n;
  925. counters.put(counterType, count);
  926. } else {
  927. counters.put(counterType, Integer.valueOf(n));
  928. }
  929. if (counterType.equals(CounterType.P1P1) || counterType.equals(CounterType.M1M1)) {
  930. checkPlusAndMinusCounters();
  931. changeCountersCommand.execute(this, getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1));
  932. }
  933. if (getGame() != null) {
  934. getGame().getEventManager().getChangeCountersEvent().notifyObservers(this);
  935. }
  936. }
  937. public void removeCounter(CounterType counterType) {
  938. removeCounter(counterType, 1);
  939. }
  940. public void resetCounters() {
  941. for (CounterType type : CounterType.values()) {
  942. this.removeCounter(type, this.getCounters(type));
  943. }
  944. }
  945. public void removeCounter(CounterType counterType, int n) {
  946. if (counters.containsKey(counterType)) {
  947. Integer count = counters.get(counterType) - n;
  948. counters.put(counterType, count);
  949. }
  950. if (counterType.equals(CounterType.P1P1) || counterType.equals(CounterType.M1M1)) {
  951. checkPlusAndMinusCounters();
  952. changeCountersCommand.execute(this, getCounters(CounterType.P1P1) - getCounters(CounterType.M1M1));
  953. }
  954. }
  955. public HashMap<CounterType, Integer> getAllCounters() {
  956. return this.counters;
  957. }
  958. @SuppressWarnings("unchecked")
  959. private void cloneCounters() {
  960. this.counters = (HashMap<CounterType, Integer>) this.counters.clone();
  961. }
  962. @SuppressWarnings("unchecked")
  963. private void cloneEquipping() {
  964. this.equipping = (ArrayList<Card>) this.equipping.clone();
  965. }
  966. @SuppressWarnings("unchecked")
  967. private void cloneAttached() {
  968. this.attached = (ArrayList<Card>) this.attached.clone();
  969. }
  970. @SuppressWarnings("unchecked")
  971. private void cloneKeywords() {
  972. this.keyword = (ArrayList<String>) this.keyword.clone();
  973. }
  974. @SuppressWarnings("unchecked")
  975. private void cloneEditProperties() throws Exception {
  976. //this.tapped = (EditableProperty<Boolean>) new Expression(this.tapped, "clone", null).getValue();
  977. }
  978. private void checkPlusAndMinusCounters() {
  979. Integer plus = getCounters(CounterType.P1P1);
  980. Integer minus = getCounters(CounterType.M1M1);
  981. if (plus > 0 && minus > 0) {
  982. if (plus >= minus) {
  983. setCounters(CounterType.P1P1, plus - minus);
  984. setCounters(CounterType.M1M1, 0);
  985. } else {
  986. setCounters(CounterType.M1M1, minus - plus);
  987. setCounters(CounterType.P1P1, 0);
  988. }
  989. }
  990. }
  991. public int getCounters(CounterType counterType) {
  992. if (counters.containsKey(counterType)) {
  993. return counters.get(counterType);
  994. } else {
  995. return 0;
  996. }
  997. }
  998. public boolean isHybrid() {
  999. return hybrid;
  1000. }
  1001. public void setHybrid(boolean b) {
  1002. hybrid = b;
  1003. }
  1004. public boolean isPlayer() {
  1005. return isPlayer;
  1006. }
  1007. public void setPlayer(boolean isPlayer) {
  1008. this.isPlayer = isPlayer;
  1009. }
  1010. private static final String manaString = "tap: add ";
  1011. public void formManaAbilities() {
  1012. formManaAbilities(false);
  1013. }
  1014. /**
  1015. * @param removeKeyword indicates that permanent shouldn't have keyword like "tap: Add {X}"
  1016. * that is used when paying for spell
  1017. * should be true for cases when there is need for additional actions, e.g. "sacrifice: add 1"
  1018. */
  1019. public void formManaAbilities(boolean removeKeyword) {
  1020. ArrayList<String> check = getKeyword();
  1021. manaAbilities.clear();
  1022. ArrayList<SpellAbility> spellAbilitiesWithMana = new ArrayList<SpellAbility>(spellAbility);
  1023. clearSpellAbility();
  1024. for (SpellAbility sa : spellAbilitiesWithMana) {
  1025. if (!(sa instanceof ManaAbility && sa.hasAspect(MagicWarsModel.ASPECT_GENERATED_ABILITY))) {
  1026. addSpellAbility(sa);
  1027. }
  1028. }
  1029. for (String keyword : check) {
  1030. if (keyword.startsWith(manaString)) {
  1031. // gets G from "tap: add G"
  1032. final String mana = keyword.substring(manaString.length()).trim();
  1033. final ManaAbility manaAbility = new ManaAbility(this) {
  1034. @Override
  1035. public void manaResolve() {
  1036. for (int i = 0; i < mana.length(); i++) {
  1037. ((GameManager)game).getPlayerById(getControllerID()).getManaPool().add(mana.substring(i,i+1));
  1038. }
  1039. tap();
  1040. }
  1041. private static final long serialVersionUID = 1L;
  1042. };
  1043. manaAbility.setDescription("{T}: Add " + mana + " to your mana pool.");
  1044. manaAbility.addAspect(MagicWarsModel.ASPECT_GENERATED_ABILITY);
  1045. addSpellAbility(manaAbility);
  1046. if (!removeKeyword) {
  1047. manaAbilities.add(mana);
  1048. }
  1049. }
  1050. }
  1051. }
  1052. public ArrayList<String> getManaAbilities() {
  1053. return manaAbilities;
  1054. }
  1055. public boolean isSuspended() {
  1056. return suspended;
  1057. }
  1058. public void setSuspended(boolean suspended) {
  1059. this.suspended = suspended;
  1060. }
  1061. public int getTimeCounters() {
  1062. return getCounters(CounterType.TIME);
  1063. }
  1064. public void setTimeCounters(int timeCounters) {
  1065. setCounters(CounterType.TIME, timeCounters);
  1066. }
  1067. public void addTimeCounters(int timeCounters) {
  1068. putCounter(CounterType.TIME, timeCounters);
  1069. }
  1070. public void removeTimeCounters(int timeCounters) {
  1071. removeCounter(CounterType.TIME, timeCounters);
  1072. }
  1073. public int getQuestCounters() {
  1074. return getCounters(CounterType.QUEST);
  1075. }
  1076. public int getLevelCounters() {
  1077. return getCounters(CounterType.LEVEL);
  1078. }
  1079. public int getChargeCounters() {
  1080. return getCounters(CounterType.CHARGE);
  1081. }
  1082. public int getFadeCounters() {
  1083. return getCounters(CounterType.FADE);
  1084. }
  1085. public boolean isBlocking() {
  1086. return isBlocking;
  1087. }
  1088. public void setBlocking(boolean isBlocking) {
  1089. this.isBlocking = isBlocking;
  1090. }
  1091. public Command getWhenPlayCommand() {
  1092. return whenPlayCommand;
  1093. }
  1094. public void whenPlayCommand() {
  1095. whenPlayCommand.execute();
  1096. }
  1097. public void setWhenPlayCommand(Command whenPlayCommand) {
  1098. this.whenPlayCommand = whenPlayCommand;
  1099. }
  1100. public void setWhenCastCommand(Command whenCastCommand) {
  1101. this.whenPlayCommand = whenCastCommand;
  1102. }
  1103. public void setChangeCountersCommand(CommandArgs changeCountersCommand) {
  1104. this.changeCountersCommand = changeCountersCommand;
  1105. }
  1106. public boolean isFlashback() {
  1107. return flashback;
  1108. }
  1109. public void setHasFlashback(boolean flashback) {
  1110. this.flashback = flashback;
  1111. }
  1112. public boolean isCanBePrevented() {
  1113. return canBePrevented;
  1114. }
  1115. public void setCanBePrevented(boolean canBePrevented) {
  1116. this.canBePrevented = canBePrevented;
  1117. }
  1118. public String getSetName() {
  1119. return setName;
  1120. }
  1121. public void setSetName(String setName) {
  1122. this.setName = setName;
  1123. }
  1124. public void setEquip(Command c) {
  1125. equipCommand = c;
  1126. }
  1127. public void equip() {
  1128. equipCommand.execute();
  1129. }
  1130. public void setUnEquip(Command c) {
  1131. unEquipCommand = c;
  1132. }
  1133. public void unEquip() {
  1134. unEquipCommand.execute();
  1135. }
  1136. public ArrayList<Card> getEquippedBy() {
  1137. return equippedBy;
  1138. }
  1139. public ArrayList<Card> getEquipping() {
  1140. return equipping;
  1141. }
  1142. public ArrayList<Card> getEnchanting() {
  1143. return equipping;
  1144. }
  1145. public boolean isEnchantedBy(Card card) {
  1146. return equipping.contains(card);
  1147. }
  1148. /**
  1149. * Can be null
  1150. * @return creature that current card equips
  1151. */
  1152. public Card getCreatureEquipped() {
  1153. if (equipping == null || equipping.size() == 0) {
  1154. return null;
  1155. }
  1156. return equipping.get(0);
  1157. }
  1158. public boolean isEquipped() {
  1159. return equippedBy.size() != 0;
  1160. }
  1161. public boolean isEquipping() {
  1162. return equipping.size() != 0;
  1163. }
  1164. public boolean isEnchanting() {
  1165. return equipping.size() != 0;
  1166. }
  1167. public void removeEquipping(Card c) {
  1168. equipping.remove(c);
  1169. // this.updateObservers();
  1170. }
  1171. public void removeEquippedBy(Card c) {
  1172. equippedBy.remove(c);
  1173. // this.updateObservers();
  1174. }
  1175. public void addEquippedBy(Card c) {
  1176. equippedBy.add(c);
  1177. // this.updateObservers();
  1178. }
  1179. /**
  1180. * Remove equipment effect. Used for equipment cards only
  1181. *
  1182. * @param c
  1183. */
  1184. public void unEquipCard(Card c) {
  1185. this.unEquip();
  1186. equipping.remove(c);
  1187. c.removeEquippedBy(this);
  1188. }
  1189. /**
  1190. * Add equipment effect. Used for equipment cards only
  1191. *
  1192. * @param c
  1193. */
  1194. public void equipCard(Card c) {
  1195. equipping.add(c);
  1196. c.addEquippedBy(this);
  1197. this.equip();
  1198. }
  1199. public EventManager getEvents() {
  1200. return getGame().getEventManager();
  1201. }
  1202. public int getPlayerIdToAsk() {
  1203. return playerIdToAsk;
  1204. }
  1205. public void setPlayerIdToAsk(int playerIdToAsk) {
  1206. this.playerIdToAsk = playerIdToAsk;
  1207. }
  1208. public int getCollectorID() {
  1209. return collectorID;
  1210. }
  1211. public void setCollectorID(int collectorID) {
  1212. this.collectorID = collectorID;
  1213. }
  1214. public Command getAsLongAsCommand() {
  1215. return asLongAsCommand;
  1216. }
  1217. public void setAsLongAsCommand(Command asLongAsCommand) {
  1218. this.asLongAsCommand = asLongAsCommand;
  1219. }
  1220. public ArrayList<String> getOracleText() {
  1221. return oracleText;
  1222. }
  1223. public void setOracleText(ArrayList<String> oracleText) {
  1224. this.oracleText = oracleText;
  1225. if (oracleText == null) {
  1226. return;
  1227. }
  1228. this.text = "";
  1229. int index = 0;
  1230. for (String t : oracleText) {
  1231. if (index > 0) {
  1232. text += "<p>";
  1233. }
  1234. text += t;
  1235. index++;
  1236. }
  1237. }
  1238. public boolean isScripted() {
  1239. return isScripted;
  1240. }
  1241. public void setScripted(boolean isScripted) {
  1242. this.isScripted = isScripted;
  1243. }
  1244. public GameManager getGame() {
  1245. return game;
  1246. }
  1247. public void setGame(GameManager game) {
  1248. this.game = game;
  1249. //tapped = new EditableProperty<Boolean>(game, null, "tap", false);
  1250. }
  1251. public String plus(String arg) {
  1252. return toString() + arg;
  1253. }
  1254. /**
  1255. * N.B. This method CAN'T BE used for "Clone" effects: Target creature becomes copy of another target creature.
  1256. * The main issue is with commands that are stateless but operate with parent card.
  1257. * So cloning them still operate with original one.
  1258. * Example: Card1 has "Card1 enters the battlefield tapped" that is implemented as EntersTheBattlefield command.
  1259. * When we try Card2 = Card1.getCopy() and Card2 enters the battlefield, it would try to tap Card1 because command points to it.
  1260. *
  1261. * There is one option is to clone objects by serialization\desearialization but it causes some problems with anonymous Command classes as it throws exception whenever the class where it is defined is not Serializable (have no "implements Serializable")
  1262. * So such implementation is very dangerous as may cause exceptions in future when developers add new classes without implementing Serializable interface.
  1263. * That's why this way was rejected.
  1264. *
  1265. * Call this method for internal use only.
  1266. *
  1267. *
  1268. * @deprecated Use {@link GameManager.copyCard(Card card)} instead.
  1269. *
  1270. * @return
  1271. */
  1272. @Deprecated
  1273. public Card getCopy() {
  1274. Card clone = null;
  1275. try {
  1276. clone = (Card)this.clone();
  1277. if (game != null) clone.setUniqueNumber(game.getUniqueCardID());
  1278. clone.cloneCounters();
  1279. clone.cloneEquipping();
  1280. clone.cloneAttached();
  1281. clone.cloneKeywords();
  1282. clone.cloneEditProperties();
  1283. } catch (CloneNotSupportedException e) {
  1284. log.error(e,e);
  1285. } catch (Exception e) {
  1286. log.error(e,e);
  1287. }
  1288. return clone;
  1289. }
  1290. public int getPreventDamage() {
  1291. return preventDamage;
  1292. }
  1293. public void setPreventDamage(int preventDamage) {
  1294. this.preventDamage = preventDamage;
  1295. }
  1296. public void setGroovyRef(GroovyScriptFacade ref) {
  1297. this.groovyRef = ref;
  1298. }
  1299. public GroovyScriptFacade getGroovyRef() {
  1300. return this.groovyRef;
  1301. }
  1302. public boolean zone(GameZone zone) {
  1303. switch(zone) {
  1304. case Battlefield:
  1305. return game.getBattlefield().isCardInPlay(this);
  1306. case Graveyard:
  1307. return game.getGraveyard().isCardInGrave(this);
  1308. case Hand:
  1309. return game.getPlayer(getOwnerID()).getHand().isCardInHand(this);
  1310. case Exile:
  1311. return game.getExile().isCardInExile(this);
  1312. case Any:
  1313. return true;
  1314. default: return false;
  1315. }
  1316. }
  1317. /**
  1318. * Reset all parameters based on original card
  1319. * @param original card to use for reseting
  1320. *
  1321. */
  1322. public void resetCard(Card original) {
  1323. setCardSubType(original.getSubType());
  1324. setCardType(original.getCardType());
  1325. setCardSuperType(original.getCardSuperType());
  1326. setKeyword(original.getKeyword());
  1327. setColors(original.getColor());
  1328. attached.clear();
  1329. ownerID = original.getOwnerID();
  1330. controllerID = ownerID;
  1331. isAttacking = false;
  1332. isBlocking = false;
  1333. //tapped.setValueSilent(false);
  1334. tapped = false;
  1335. sickness = true;
  1336. equipping.clear();
  1337. equippedBy.clear();
  1338. damage = 0;
  1339. preventUntapCounters = 0;
  1340. flashback = false;
  1341. controllerID = original.getControllerID();
  1342. power = original.getAttack();
  1343. toughness = original.getDefense();
  1344. pumpAttack = 0;
  1345. pumpDefense = 0;
  1346. powerToughnessIsSwitched = false;
  1347. resetCounters();
  1348. for (Entry<CounterType, Integer> e : original.getAllCounters().entrySet()) {
  1349. counters.put(e.getKey(), e.getValue());
  1350. }
  1351. Map<String, List<Object>> aspects = getAspects();
  1352. setAspects(original.getAspects());
  1353. for (String a : aspects.keySet()) {
  1354. if (a.startsWith(MagicWarsModel.PERMANENT_ASPECT)) {
  1355. addAspect(a);
  1356. }
  1357. }
  1358. formManaAbilities();
  1359. }
  1360. private static final long serialVersionUID = 2L;
  1361. }