1 /** 2 * Represents a deck of cards. You can add cards to the deck and take them away, 3 * along with all the other things you would expect of a deck of cards. The deck 4 * takes a type as an argument. 5 */ 6 function Deck(type, x, y, options,zOffset) { 7 options = options || {}; 8 9 var filter = options.filter || function (card) { 10 return true; 11 };// Limits which cards can be added to the top of the deck. 12 var cards = []; 13 14 var observers = [];// The observers observing this deck! 15 if (options.draggable === false) { 16 var draggable = false; 17 } else { 18 draggable = true; 19 } 20 var action = options.action || null;// The action associated with this deck. 21 var paused = false;// When this is paused, it does not send out any events. 22 23 this.getzOffset=function() { 24 return zOffset||0; 25 }; 26 27 var active = active || false; 28 29 /** 30 * Pauses the deck, causing it to not send any more events out. 31 */ 32 this.pause = function () { 33 paused = true; 34 }; 35 36 /** 37 * Unpauses the deck, causing it to send events out once more. 38 */ 39 this.unpause = function () { 40 paused = false; 41 }; 42 43 /** 44 * Shuffles the deck. 45 */ 46 this.shuffle = function () { 47 var newCards = []; 48 49 while (cards.length > 0) { 50 var index = Math.floor(Math.random() * cards.length); 51 newCards.push(cards[index]); 52 cards.splice(index, 1); 53 } 54 55 cards = newCards; 56 57 this.fire({ 58 type : "shuffle", 59 card : null 60 }); 61 }; 62 63 /** 64 * Moves the top n cards from this deck to the specified deck. If there are 65 * less than n cards in this deck, deals as many as possible. This can 66 * optionally deal the cards face down. You can deal even to a deck that 67 * cannot be added to by other methods. If bottom is true, deals to the 68 * bottom of the target deck rather than the top. 69 */ 70 this.deal = function (deck, n, facedown, bottom, nonPausing) { 71 if(!nonPausing) { 72 this.pause(); 73 } 74 var filter = deck.getFilter(); 75 deck.setFilter(function () { 76 return true; 77 }); 78 79 if (!n) { 80 return; 81 } 82 83 for (var i = 0; i < n; i++) { 84 var card = this.peek(); 85 if (!this.peek()) { 86 break; 87 } 88 89 if (i == n - 1&&!nonPausing) { 90 this.unpause(); 91 } 92 93 this.remove(card); 94 if (facedown) { 95 card.setFaceUp(false); 96 } else if (facedown === false) { 97 card.setFaceUp(true); 98 } 99 bottom ? deck.addBottom(card) : deck.addTop(card); 100 } 101 102 deck.unpause(); 103 deck.fire({ 104 type : "add" 105 }); 106 107 deck.setFilter(filter); 108 }; 109 110 /** 111 * Flips the top card of the deck. If the top card is face up, turns it face 112 * down; otherwise, turns it face up. 113 */ 114 this.flipTopCard = function () { 115 var card = this.peek(); 116 card.setFaceUp(!card.isFaceUp()); 117 118 this.fire({ 119 type : "flip", 120 card : card 121 }); 122 }; 123 124 /** 125 * Returns the number of cards in the deck. 126 */ 127 this.getSize = function () { 128 return cards.length; 129 }; 130 131 /** 132 * Adds the given card to the top of the deck. Returns true if the card was 133 * added and false otherwise. 134 */ 135 this.addTop = function (card, nofilter) { 136 if (nofilter||filter(card)) { 137 cards.push(card); 138 139 this.fire({ 140 type : "add", 141 card : card 142 }); 143 144 return true; 145 } else { 146 return false; 147 } 148 }; 149 150 /** 151 * Adds the given card to the bottom of the deck. 152 */ 153 this.addBottom = function (card) { 154 if (card!=undefined&&(filter(card))) { 155 cards.unshift(card); 156 157 this.fire({ 158 type : "add", 159 card : card 160 }); 161 162 return true; 163 } else { 164 return false; 165 } 166 }; 167 168 /** 169 * Removes the given card from the deck. If the card is not in the deck, 170 * nothing happens. 171 */ 172 this.remove = function (card) { 173 for (var i = 0; i < cards.length; i++) { 174 if (cards[i] == card) { 175 cards.splice(i, 1); 176 } 177 } 178 179 this.fire({ 180 type : "remove", 181 card : card 182 }); 183 184 return card; 185 }; 186 187 /** 188 * Returns the top card of the deck without changing anything. 189 */ 190 this.peek = function () { 191 return cards[cards.length - 1]; 192 }; 193 194 /** 195 * Returns all of the cards currently in the deck. 196 */ 197 this.getCards = function () { 198 return cards; 199 }; 200 201 /** 202 * Replaces all of the cards in the deck with the standard 52 cards. 203 */ 204 this.initialize = function (facedown) { 205 cards = []; 206 207 for (var i = 1; i <= 13; i++) { 208 cards.push(new Card(i, "s")); 209 cards.push(new Card(i, "h")); 210 cards.push(new Card(i, "d")); 211 cards.push(new Card(i, "c")); 212 } 213 214 this.fire({ 215 type : "reset", 216 card : null 217 }); 218 219 if (facedown) { 220 for (i = 0; i < cards.length; i++) { 221 cards[i].setFaceUp(false); 222 } 223 } 224 }; 225 226 /** 227 * Returns the x position of this deck in magical card units. If there is no 228 * position, returns undefined. 229 */ 230 this.getX = function () { 231 return x; 232 }; 233 234 /** 235 * Returns the y position of this deck in magical card units. If there is no 236 * position, returns undefined. 237 */ 238 this.getY = function () { 239 return y; 240 }; 241 242 /** 243 * Sets the x position of this deck in magical card units. 244 */ 245 this.setX = function (newX) { 246 x = newX; 247 248 this.fire({ 249 type : "moved" 250 }); 251 }; 252 253 /** 254 * Sets the y position of this deck in magical card units. 255 */ 256 this.setY = function (newY) { 257 y = newY; 258 259 this.fire({ 260 type : "moved" 261 }); 262 }; 263 264 /** 265 * Returns this deck's current filter. 266 */ 267 this.getFilter = function () { 268 return filter; 269 }; 270 271 /** 272 * Sets the filter to a new function. The filter function should take a card 273 * and return true if the given card can be added to the top and false 274 * otherwise. The filter then also takes the deck the drag came from and the 275 * size of the pile being dragged. 276 */ 277 this.setFilter = function (newFilter) { 278 filter = newFilter; 279 }; 280 281 /** 282 * Returns the action associated with this deck. The action is something 283 * that should be done when it is click, for example. If there is no action, 284 * null is returned. 285 */ 286 this.getAction = function () { 287 return action; 288 }; 289 290 /** 291 * Sets the action associated with this deck. 292 */ 293 this.setAction = function (newAction) { 294 action = newAction; 295 }; 296 297 /** 298 * Returns whether the cards from this pile should be draggable. 299 */ 300 this.isDraggable = function () { 301 return draggable; 302 }; 303 304 /** 305 * Sets whether the cards from this pile should be draggable. 306 */ 307 this.setDraggable = function (flag) { 308 draggable = flag; 309 }; 310 311 /** 312 * Returns what type of deck this is. The type is information for the UI 313 * regarding how to treat the deck; it does not affect gameplay. 314 */ 315 this.getType = function () { 316 return type; 317 }; 318 319 /** 320 * Sets what type of deck this is. The type is information for the UI 321 * regarding how to treat the deck; it does not affect gameplay. 322 */ 323 this.setType = function (newType) { 324 type = newType; 325 }; 326 327 /** 328 * Fires the given event by passing it to every observer. The event should 329 * have a type like "remove" or "add" and the card that changed. If more 330 * than one card changed, it should have an array of cards. 331 */ 332 this.fire = function (event) { 333 if (!paused) { 334 for (var i = 0; i < observers.length; i++) { 335 try { 336 observers[i](event); 337 } catch (e) { 338 // Remove the offending observer from the observers list: 339 observers.splice(i, 1); 340 } 341 } 342 } 343 }; 344 345 /** 346 * Adds an observer to the game. The observer should be a function accepting 347 * an event that will be called every time there is a change to the game. 348 */ 349 this.observe = function (observer) { 350 observers.push(observer); 351 }; 352 353 /** 354 * Removes an occurence of the specified observer from the observers list. 355 * If the given observer is in the list twice, only the first instance is 356 * removed. If the given observer is not in the list, nothing happens. 357 */ 358 this.removeObserver = function (observer) { 359 for (var i = 0; i < observers.length; i++) { 360 if (observers[i] == observer) { 361 observers.splice(i, 1); 362 return; 363 } 364 } 365 }; 366 367 /** 368 * Returns whether this is the "active" hand--that is, whether it is the 369 * human player's hand. 370 */ 371 this.isActive = function () { 372 return active; 373 }; 374 375 /** 376 * Sets whether this deck is the "active" hand. 377 */ 378 this.setActive = function (flag) { 379 active = flag; 380 }; 381 } 382