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