1#!/usr/bin/env python 2# -*- mode: python; coding: utf-8; -*- 3# ---------------------------------------------------------------------------## 4# 5# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer 6# Copyright (C) 2003 Mt. Hood Playing Card Co. 7# Copyright (C) 2005-2009 Skomoroh 8# 9# This program is free software: you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation, either version 3 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program. If not, see <http://www.gnu.org/licenses/>. 21# 22# ---------------------------------------------------------------------------## 23 24 25# imports 26 27# PySol imports 28from pysollib.mfxutil import Struct 29from pysollib.pysoltk import MfxCanvasText 30 31 32# ************************************************************************ 33# * a helper class to create common layouts 34# ************************************************************************ 35 36# a layout stack 37class _LayoutStack: 38 def __init__(self, x, y, suit=None): 39 self.x = int(round(x)) 40 self.y = int(round(y)) 41 self.suit = suit 42 self.text_args = {} 43 self.text_format = "%d" 44 45 def setText(self, x, y, anchor="center", format=None, **kw): 46 self.text_args["x"] = x 47 self.text_args["y"] = y 48 self.text_args["anchor"] = anchor 49 self.text_args.update(kw) 50 if format is not None: 51 self.text_format = format 52 53 54class Layout: 55 def __init__(self, game, card_x_space=None, card_y_space=None, **kw): 56 self.game = game 57 self.canvas = self.game.canvas 58 self.size = None 59 self.s = Struct( 60 talon=None, 61 waste=None, 62 foundations=[], 63 rows=[], 64 reserves=[], 65 ) 66 self.stackmap = {} 67 self.regions = [] 68 # set visual constants 69 images = self.game.app.images 70 71 layout_x_margin = images.CARDW // 9 72 layout_y_margin = layout_x_margin 73 layout_card_x_space = images.CARDW // 9 74 layout_card_y_space = images.CARDH // 8 75 76 self.CW = images.CARDW 77 self.CH = images.CARDH 78 self.XOFFSET = images.CARD_XOFFSET 79 self.YOFFSET = images.CARD_YOFFSET 80 self.XM = layout_x_margin # XMARGIN 81 self.YM = layout_y_margin # YMARGIN 82 83 if card_x_space is None: 84 self.XS = self.CW + layout_card_x_space # XSPACE 85 else: 86 self.XS = self.CW + card_x_space 87 if card_y_space is None: 88 self.YS = self.CH + layout_card_y_space # YSPACE 89 else: 90 self.YS = self.CH + card_y_space 91 92 # self.CARD_X_SPACE = layout_card_x_space 93 # self.CARD_Y_SPACE = layout_card_y_space 94 # self.RIGHT_MARGIN = layout_x_margin-layout_card_x_space 95 # self.BOTTOM_MARGIN = layout_y_margin-layout_card_y_space 96 97 font = game.app.getFont("canvas_default") 98 # self.TEXT_MARGIN = 10 99 self.TEXT_MARGIN = font[1] 100 # self.TEXT_HEIGHT = 30 101 self.TEXT_HEIGHT = 18+font[1] 102 103 self.__dict__.update(kw) 104 if self.game.preview > 1: 105 if "XOFFSET" in kw: 106 self.XOFFSET //= self.game.preview 107 if "YOFFSET" in kw: 108 self.YOFFSET //= self.game.preview 109 self.TEXT_HEIGHT = 10 110 111 def __createStack(self, x, y, suit=None): 112 stack = _LayoutStack(x, y, suit) 113 mapkey = (stack.x, stack.y) 114 # from pprint import pprint 115 # print mapkey 116 # pprint(self.stackmap) 117 assert mapkey not in self.stackmap 118 self.stackmap[mapkey] = stack 119 return stack 120 121 def _setText(self, stack, anchor="center"): 122 tx, ty, ta, tf = self.getTextAttr(stack, anchor) 123 stack.setText(tx, ty, ta, tf) 124 125 # 126 # 127 # 128 129 def createGame(self, layout_method, 130 talon_class=None, 131 waste_class=None, 132 foundation_class=None, 133 row_class=None, 134 reserve_class=None, 135 **kw 136 ): 137 # create layout 138 game = self.game 139 s = game.s 140 layout_method(self, **kw) 141 game.setSize(self.size[0], self.size[1]) 142 # create stacks 143 if talon_class: 144 s.talon = talon_class(self.s.talon.x, self.s.talon.y, game) 145 if waste_class: 146 s.waste = waste_class(self.s.waste.x, self.s.waste.y, game) 147 if foundation_class: 148 if isinstance(foundation_class, (list, tuple)): 149 n = len(self.s.foundations)//len(foundation_class) 150 i = 0 151 for j in range(n): 152 for cls in foundation_class: 153 r = self.s.foundations[i] 154 s.foundations.append(cls(r.x, r.y, game, suit=r.suit)) 155 i += 1 156 157 else: 158 for r in self.s.foundations: 159 s.foundations.append(foundation_class(r.x, r.y, game, 160 suit=r.suit)) 161 if row_class: 162 for r in self.s.rows: 163 s.rows.append(row_class(r.x, r.y, game)) 164 if reserve_class: 165 for r in self.s.reserves: 166 s.reserves.append(reserve_class(r.x, r.y, game)) 167 # default 168 self.defaultAll() 169 # reserves texts 170 if self.s.reserves and ('reserve_texts' in kw) and kw['reserve_texts']: 171 game = self.game 172 for i in range(len(game.s.reserves)): 173 s1 = game.s.reserves[i] 174 s2 = self.s.reserves[i] 175 s1.texts.ncards = self.defaultText(s2) 176 177 # 178 # public util for use by class Game 179 # 180 181 def getTextAttr(self, stack, anchor): 182 x, y = 0, 0 183 if stack is not None: 184 x, y = stack.x, stack.y 185 delta_x, delta_y = 4, 4 186 delta_yy = 10 187 d = { 188 "n": (x+self.CW//2, y-delta_y, "s", "%d"), 189 "nn": (x+self.CW//2, y-delta_yy, "s", "%d"), 190 "s": (x+self.CW//2, y+self.CH+delta_y, "n", "%d"), 191 "ss": (x+self.CW//2, y+self.CH+delta_yy, "n", "%d"), 192 "nw": (x-delta_x, y, "ne", "%d"), 193 "sw": (x-delta_x, y+self.CH, "se", "%d"), 194 "ne": (x+self.CW+delta_x, y, "nw", "%d"), 195 "se": (x+self.CW+delta_x, y+self.CH, "sw", "%d"), 196 "w": (x-delta_x, y+self.CH//2, "e", "%d"), 197 "e": (x+self.CW+delta_x, y+self.CH//2, "w", "%d"), 198 } 199 return d[anchor] 200 201 def createText(self, stack, anchor, dx=0, dy=0, text_format=""): 202 if self.canvas.preview > 1: 203 return 204 assert stack.texts.ncards is None 205 tx, ty, ta, tf = self.getTextAttr(stack, anchor) 206 font = self.game.app.getFont("canvas_default") 207 stack.texts.ncards = MfxCanvasText(self.canvas, tx+dx, ty+dy, 208 anchor=ta, font=font) 209 stack.texts.ncards.text_format = text_format or tf 210 211 def createRoundText(self, stack, anchor, dx=0, dy=0): 212 if self.canvas.preview > 1: 213 return 214 assert stack.texts.rounds is None 215 delta_x, delta_y = 0, 0 216 if anchor == 'nnn': 217 anchor = 'nn' 218 delta_y = -self.TEXT_MARGIN 219 elif anchor == 'sss': 220 anchor = 'ss' 221 delta_y = self.TEXT_MARGIN 222 tx, ty, ta, tf = self.getTextAttr(stack, anchor) 223 tx += delta_x + dx 224 ty += delta_y + dy 225 font = self.game.app.getFont("canvas_default") 226 stack.texts.rounds = MfxCanvasText(self.canvas, tx, ty, 227 anchor=ta, font=font) 228 229 def setRegion(self, stacks, rects): 230 self.regions.append((stacks, rects)) 231 232 # 233 # util for use by a Game 234 # 235 236 def defaultAll(self): 237 game = self.game 238 # create texts 239 if game.s.talon: 240 game.s.talon.texts.ncards = self.defaultText(self.s.talon) 241 if game.s.waste: 242 game.s.waste.texts.ncards = self.defaultText(self.s.waste) 243 # define stack-groups 244 self.defaultStackGroups() 245 # set regions 246 self.defaultRegions() 247 248 def defaultText(self, layout_stack): 249 if self.canvas.preview > 1: 250 return None 251 # print layout_stack, layout_stack.text_args 252 if layout_stack is None or not layout_stack.text_args: 253 return None 254 layout_stack.text_args["font"] = \ 255 self.game.app.getFont("canvas_default") 256 t = MfxCanvasText(self.game.canvas, **layout_stack.text_args) 257 t.text_format = layout_stack.text_format 258 return t 259 260 # define stack-groups 261 def defaultStackGroups(self): 262 game = self.game 263 waste = [] 264 if game.s.waste is not None: 265 waste = [game.s.waste] 266 game.sg.talonstacks = [game.s.talon] + waste 267 game.sg.dropstacks = game.s.rows + game.s.reserves + waste 268 game.sg.openstacks = game.s.foundations + game.s.rows + game.s.reserves 269 game.sg.reservestacks = game.s.reserves 270 271 def defaultRegions(self): 272 for region in self.regions: 273 # convert layout-stacks to corresponding game-stacks 274 stacks = [] 275 for s in region[0]: 276 mapkey = (s.x, s.y) 277 id = self.game.stackmap[mapkey] 278 stacks.append(self.game.allstacks[id]) 279 # print stacks, region[1] 280 self.game.setRegion(stacks, region[1]) 281 282 # 283 # Baker's Dozen layout 284 # - left: 2 rows 285 # - right: foundations, talon 286 # 287 288 def bakersDozenLayout(self, rows, texts=0, playcards=9): 289 S = self.__createStack 290 CW, CH = self.CW, self.CH 291 XM, YM = self.XM, self.YM 292 XS, YS = self.XS, self.YS 293 294 decks = self.game.gameinfo.decks 295 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 296 halfrows = (rows + 1) // 2 297 298 # set size so that at least 9 cards are fully playable 299 h = YS + min(2*YS, (playcards-1)*self.YOFFSET) 300 h = max(h, 5*YS//2, 3*YS//2+CH) 301 h = min(h, 3*YS) 302 303 # create rows 304 x, y = XM, YM 305 for i in range(halfrows): 306 self.s.rows.append(S(x+i*XS, y)) 307 for i in range(rows-halfrows): 308 self.s.rows.append(S(x+i*XS, y+h)) 309 310 # create foundations 311 x, y = XM + halfrows * XS, YM 312 self.setRegion(self.s.rows, (-999, -999, x - CW // 2, 999999)) 313 for suit in range(suits): 314 for i in range(decks): 315 self.s.foundations.append(S(x+i*XS, y, suit=suit)) 316 y += YS 317 318 # create talon 319 h = YM + 2*h 320 self.s.talon = S(x, h - YS) 321 if texts: 322 assert 0 323 324 # set window 325 self.size = (XM + (halfrows+decks)*XS, h) 326 327 # 328 # FreeCell layout 329 # - top: free cells, foundations 330 # - below: rows 331 # - left bottom: talon, waste 332 # 333 334 def freeCellLayout(self, rows=0, reserves=0, waste=0, 335 texts=0, reserve_texts=False, playcards=18): 336 S = self.__createStack 337 CH = self.CH 338 XM, YM = self.XM, self.YM 339 XS, YS = self.XS, self.YS 340 341 decks = self.game.gameinfo.decks 342 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 343 toprows = suits*decks 344 if reserves: 345 toprows += reserves+1 346 maxrows = max(rows, toprows) 347 348 w = XM + maxrows*XS 349 350 # set size so that at least 2//3 of a card is visible with 18 cards 351 h = CH*2//3 + (playcards-1)*self.YOFFSET 352 h = YM + YS + max(h, 3*YS) 353 if reserves and reserve_texts: 354 h += self.TEXT_HEIGHT 355 356 # create reserves & foundations 357 x, y = (w - (toprows*XS - XM))//2, YM 358 if reserves: 359 for i in range(reserves): 360 s = S(x, y) 361 self.s.reserves.append(s) 362 if reserve_texts: 363 self._setText(s, anchor="s") 364 x += XS 365 x += XS 366 for suit in range(suits): 367 for i in range(decks): 368 self.s.foundations.append(S(x, y, suit=suit)) 369 x += XS 370 371 # create rows 372 x, y = (w - (rows*XS - XM))//2, YM + YS 373 if reserves and reserve_texts: 374 y += self.TEXT_HEIGHT 375 for i in range(rows): 376 self.s.rows.append(S(x, y)) 377 x += XS 378 self.setRegion(self.s.rows, (-999, y - CH // 2, 999999, 999999)) 379 380 # create talon 381 x, y = XM, h - YS 382 self.s.talon = s = S(x, y) 383 if texts: 384 if waste: 385 # place text top of stack 386 self._setText(s, anchor="n") 387 else: 388 # place text right of stack 389 self._setText(s, anchor="se") 390 if waste: 391 x += XS 392 self.s.waste = s = S(x, y) 393 if texts: 394 # place text top of stack 395 self._setText(s, anchor="n") 396 397 # set window 398 self.size = (w, h) 399 400 # 401 # Gypsy layout 402 # - left: rows 403 # - right: foundations, talon 404 # - bottom: reserves 405 # 406 407 def gypsyLayout(self, rows, waste=0, reserves=0, 408 texts=1, reserve_texts=False, round_text=False, 409 playcards=25): 410 S = self.__createStack 411 CW, CH = self.CW, self.CH 412 XM, YM = self.XM, self.YM 413 XS, YS = self.XS, self.YS 414 415 decks = self.game.gameinfo.decks 416 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 417 418 w = XM + max(rows+decks, reserves+2+waste)*XS 419 if reserves: 420 h = YS+(playcards-1)*self.YOFFSET+YS 421 else: 422 # set size so that at least 2//3 of a card is visible with 25 cards 423 h = CH*2//3 + (playcards-1)*self.YOFFSET 424 h = YM + max(h, (suits+1)*YS) 425 if reserves and reserve_texts: 426 h += self.TEXT_HEIGHT 427 428 # create rows 429 x, y = XM, YM 430 for i in range(rows): 431 self.s.rows.append(S(x, y)) 432 x += XS 433 if reserves: 434 yy = h - YS - CH//2 435 else: 436 yy = 999999 437 self.setRegion(self.s.rows, (-999, -999, x - CW // 2, yy)) 438 439 # create foundations 440 x = w - decks*XS 441 for suit in range(suits): 442 for i in range(decks): 443 self.s.foundations.append(S(x+i*XS, y, suit=suit)) 444 y += YS 445 446 # create talon and waste 447 x, y = x + (decks-1)*XS, h - YS 448 if texts: 449 x -= XS//2 450 self.s.talon = s = S(x, y) 451 anchor = 's' 452 if round_text: 453 anchor = 'n' 454 if texts: 455 # place text right of stack 456 self._setText(s, anchor=anchor+"e") 457 if waste: 458 x -= XS 459 self.s.waste = s = S(x, y) 460 if texts: 461 # place text left of stack 462 self._setText(s, anchor=anchor+"w") 463 # create reserves 464 x, y = XM, h-YS 465 for i in range(reserves): 466 s = S(x, y) 467 self.s.reserves.append(s) 468 if reserve_texts: 469 self._setText(s, anchor="n") 470 x += XS 471 472 # set window 473 self.size = (w, h) 474 475 # 476 # Harp layout 477 # - top: reserves, rows 478 # - bottom: foundations, waste, talon 479 # 480 481 def harpLayout(self, rows, waste, reserves=0, 482 texts=1, reserve_texts=False, playcards=19): 483 S = self.__createStack 484 CH = self.CH 485 XM, YM = self.XM, self.YM 486 XS, YS = self.XS, self.YS 487 488 decks = self.game.gameinfo.decks 489 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 490 491 w = max(reserves*XS, rows*XS, (suits*decks+waste+1)*XS, 492 (suits*decks+1)*XS+2*XM) 493 w += XM 494 495 # set size so that at least 19 cards are fully playable 496 h = YS + (playcards-1)*self.YOFFSET 497 h = max(h, 3*YS) 498 if texts: 499 h += self.TEXT_HEIGHT 500 if reserves: 501 h += YS 502 if reserves and reserve_texts: 503 h += self.TEXT_HEIGHT 504 505 # top 506 y = YM 507 if reserves: 508 if reserve_texts: 509 y += self.TEXT_HEIGHT 510 x = (w - (reserves*XS - XM))//2 511 for i in range(reserves): 512 s = S(x, y) 513 self.s.reserves.append(s) 514 x += XS 515 if reserve_texts: 516 self._setText(s, anchor="n") 517 y += YS 518 x = (w - (rows*XS - XM))//2 519 for i in range(rows): 520 self.s.rows.append(S(x, y)) 521 x += XS 522 523 # bottom 524 x, y = XM, YM + h 525 for suit in range(suits): 526 for i in range(decks): 527 self.s.foundations.append(S(x, y, suit=suit)) 528 x += XS 529 if reserves: 530 yy = YM + YS - CH//2 531 if reserve_texts: 532 yy += self.TEXT_HEIGHT 533 else: 534 yy = -999 535 self.setRegion(self.s.rows, (-999, yy, 999999, y - YS // 2)) 536 if waste: 537 x = w - 2*XS 538 self.s.waste = s = S(x, y) 539 if texts: 540 # place text above stack 541 self._setText(s, 'n') 542 x = w - XS 543 self.s.talon = s = S(x, y) 544 if texts: 545 # place text above stack 546 self._setText(s, 'n') 547 548 # set window 549 self.size = (w, YM + h + YS) 550 551 # 552 # Klondike layout 553 # - top: talon, waste, foundations 554 # - below: rows 555 # - bottom: reserves 556 # 557 558 def klondikeLayout(self, rows=0, waste=0, reserves=0, 559 texts=1, reserve_texts=False, round_text=False, 560 playcards=16, center=1, text_height=0): 561 S = self.__createStack 562 CH = self.CH 563 XM, YM = self.XM, self.YM 564 XS, YS = self.XS, self.YS 565 566 decks = self.game.gameinfo.decks 567 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 568 foundrows = 1 + (suits > 5) 569 frows = decks * suits // foundrows 570 toprows = 1 + waste + frows 571 if round_text: 572 toprows += 1 573 maxrows = max(rows, toprows, reserves) 574 575 w = XM + maxrows * XS 576 # set size so that at least 2//3 of a card is visible with 16 cards 577 h = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 578 h = max(h, 2 * YS) 579 h += YM + YS * foundrows 580 if reserves and reserve_texts: 581 h += self.TEXT_HEIGHT 582 583 # top 584 # text_height = 0 585 x, y = XM, YM 586 self.s.talon = s = S(x, y) 587 if texts: 588 if waste or not center or maxrows - frows <= 1: 589 # place text below stack 590 self._setText(s, 's') 591 text_height = self.TEXT_HEIGHT 592 else: 593 # place text right of stack 594 self._setText(s, 'ne') 595 if waste: 596 x += XS 597 self.s.waste = s = S(x, y) 598 if texts: 599 # place text below stack 600 self._setText(s, 's') 601 text_height = self.TEXT_HEIGHT 602 603 for row in range(foundrows): 604 x = w - frows * XS 605 if center and frows + 2 * (1 + waste + 1) <= maxrows: 606 # center the foundations 607 x = XM + (maxrows - frows) * XS // 2 608 for suit in range(suits // foundrows): 609 for i in range(decks): 610 self.s.foundations.append( 611 S(x, y, suit=suit + (row * (suits // 2)))) 612 x += XS 613 y += YS 614 615 # below 616 x = XM 617 if rows < maxrows: 618 x += (maxrows-rows) * XS//2 619 # y += YM * (3 - foundrows) 620 y += text_height 621 for i in range(rows): 622 self.s.rows.append(S(x, y)) 623 x += XS 624 if reserves: 625 yy = h - CH//2 626 else: 627 yy = 999999 628 self.setRegion(self.s.rows, (-999, y-CH//2, 999999, yy)) 629 630 # bottom 631 if reserves: 632 x = (maxrows-reserves)*XS//2 633 y = h 634 h += YS 635 for i in range(reserves): 636 s = S(x, y) 637 self.s.reserves.append(s) 638 x += XS 639 if reserve_texts: 640 self._setText(s, anchor="n") 641 642 # set window 643 self.size = (w, h) 644 645 # 646 # Yukon layout 647 # - left: rows 648 # - right: foundations 649 # - left bottom: talon 650 # 651 652 def yukonLayout(self, rows, texts=0, playcards=20): 653 S = self.__createStack 654 CW, CH = self.CW, self.CH 655 XM, YM = self.XM, self.YM 656 XS, YS = self.XS, self.YS 657 658 decks = self.game.gameinfo.decks 659 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 660 661 # set size so that at least 2//3 of a card is visible with 20 cards 662 h = CH*2//3 + (playcards-1)*self.YOFFSET 663 h = YM + max(h, suits*YS) 664 665 # create rows 666 x, y = XM, YM 667 for i in range(rows): 668 self.s.rows.append(S(x, y)) 669 x += XS 670 self.setRegion(self.s.rows, (-999, -999, x - CW // 2, 999999)) 671 672 # create foundations 673 for suit in range(suits): 674 for i in range(decks): 675 self.s.foundations.append(S(x+i*XS, y, suit=suit)) 676 y += YS 677 678 # create talon 679 x, y = XM, h - YS 680 self.s.talon = s = S(x, y) 681 if texts: 682 # place text right of stack 683 self._setText(s, 'se') 684 685 # set window 686 self.size = (XM + (rows+decks)*XS, h) 687 688 # 689 # Easy layout 690 # - top: talon, waste, foundations 691 # - bottom: rows 692 # 693 694 def easyLayout(self, rows, waste, texts=1, playcards=10, center=1): 695 S = self.__createStack 696 CH = self.CH 697 XM, YM = self.XM, self.YM 698 XS, YS = self.XS, self.YS 699 700 decks = self.game.gameinfo.decks 701 ranks = len(self.game.gameinfo.ranks) 702 frows = 4 * decks // (1 + (decks >= 3)) 703 toprows = 1 + waste + frows 704 maxrows = max(rows, toprows) 705 yextra = 0 706 707 # set size so that at least 2//3 of a card is visible with 10 cards 708 h = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 709 h = max(h, 2 * YS) 710 711 # top 712 x, y = XM, YM 713 self.s.talon = s = S(x, y) 714 if texts: 715 if waste or not center or maxrows - frows <= 1: 716 # place text below stack 717 self._setText(s, 's') 718 yextra = 20 719 else: 720 # place text right of stack 721 self._setText(s, 'ne') 722 if waste: 723 x += XS 724 self.s.waste = s = S(x, y) 725 if texts: 726 # place text below stack 727 self._setText(s, 's') 728 x = XM + (maxrows - frows) * XS 729 if center and frows + 2 * (1 + waste + 1) <= maxrows: 730 # center the foundations 731 x = XM + (maxrows - frows) * XS // 2 732 733 x0, y0 = x, y 734 for i in range(decks): 735 for rank in range(ranks): 736 self.s.foundations.append(S(x0, y0, suit=rank)) 737 x0 += XS 738 if i == 1 and decks > 2: 739 x0, y0 = x, y + YS 740 y = y0 741 742 # bottom 743 x, y = XM, y + YS + yextra * (decks <= 2) 744 self.setRegion(self.s.rows, (-999, y - YM // 2, 999999, 999999)) 745 for i in range(rows): 746 self.s.rows.append(S(x, y)) 747 x += XS 748 749 # set window 750 self.size = (XM + maxrows * XS, YM + YS + yextra + h) 751 752 # 753 # Samuri layout 754 # - top center: rows 755 # - left & right: foundations 756 # - bottom center: talon 757 # 758 759 def samuriLayout(self, rows, waste, texts=1, playcards=20, center=1): 760 S = self.__createStack 761 CH = self.CH 762 XM, YM = self.XM, self.YM 763 XS, YS = self.XS, self.YS 764 765 decks = self.game.gameinfo.decks 766 toprows = 2 * decks + rows 767 yextra = 0 768 769 # set size so that at least 2//3 of a card is visible with 20 cards 770 h = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 771 h = max(h, 2 * YS) 772 773 # bottom center 774 x = (XM + (toprows * XS) // 2) - XS 775 y = h 776 self.s.talon = s = S(x, y) 777 if texts: 778 if waste or not center or toprows - rows <= 1: 779 # place text below stack 780 self._setText(s, 's') 781 yextra = 20 782 else: 783 # place text right of stack 784 self._setText(s, 'ne') 785 if waste: 786 x += XS 787 self.s.waste = s = S(x, y) 788 if texts: 789 # place text below stack 790 self._setText(s, 's') 791 792 # left & right 793 x, y = XM, YM 794 d, x0, y0 = 0, x, y 795 for suit in range(12): 796 for i in range(decks): 797 x0, y0 = x + XS * i, y + YS * d 798 self.s.foundations.append(S(x0, y0, suit=suit)) 799 if i == decks - 1 and suit == 5: 800 x0, y0 = x + XS * (toprows - decks), YM 801 d, x, y = -1, x0, y0 802 d += 1 803 804 # top center 805 x, y = XM + XS * decks, YM 806 self.setRegion(self.s.rows, (x - XM // 2, 0, x + XS * rows, 999999)) 807 for i in range(rows): 808 self.s.rows.append(S(x, y)) 809 x += XS 810 811 # set window 812 self.size = (XM + toprows * XS, YM + YS + yextra + h) 813 814 # 815 # Sumo layout 816 # - top center: rows 817 # - left & right: foundations 818 # - bottom center: talon 819 # 820 821 def sumoLayout(self, rows, reserves, texts=0, playcards=12, center=1): 822 S = self.__createStack 823 CH = self.CH 824 XM, YM = self.XM, self.YM 825 XS, YS = self.XS, self.YS 826 827 decks = self.game.gameinfo.decks 828 assert reserves % 2 == 0 829 toprows = 12 830 maxrows = max(rows, toprows) 831 w = XM + maxrows * XS 832 833 # set size so that at least 2//3 of a card is visible with 12 cards 834 h = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 835 h = max(h, 2 * YS) 836 837 # create foundations 838 x, y = XM, YM 839 for i in range(decks): 840 for suit in range(12): 841 self.s.foundations.append(S(x, y, suit=suit)) 842 x += XS 843 x, y = XM, y + YS 844 845 # create rows 846 x, y = XM + XS * ((toprows - rows) // 2), YM + YS * decks 847 for i in range(rows): 848 self.s.rows.append(S(x, y)) 849 x += XS 850 self.setRegion( 851 self.s.rows, 852 (XS + XM // 2, YS * decks + YM // 2, XS * 11 - XM // 2, 999999)) 853 854 # create reserves 855 x, y = XM, YM + YS * decks 856 for i in range(reserves // 2): 857 self.s.reserves.append(S(x, y)) 858 y += YS 859 x, y = w - XS, YM + YS * decks 860 for i in range(reserves // 2): 861 self.s.reserves.append(S(x, y)) 862 y += YS 863 864 # create talon 865 x, y = XM, h + YM 866 self.s.talon = s = S(x, y) 867 if texts: 868 # place text right of stack 869 self._setText(s, 'se') 870 871 # set window 872 self.size = (XM + toprows * XS, YM + YS + h) 873 874 # 875 # Fun layout 876 # - top: rows 877 # - right: foundations 878 # - bottom right: reserves 879 # 880 881 def funLayout(self, rows, reserves, texts=0, playcards=12, center=1): 882 S = self.__createStack 883 CH = self.CH 884 XM, YM = self.XM, self.YM 885 XS, YS = self.XS, self.YS 886 887 decks = self.game.gameinfo.decks 888 ranks = len(self.game.gameinfo.ranks) 889 assert rows % 2 == 0 890 assert reserves % decks == 0 891 toprows = decks + rows // 2 892 w = XM * 2 + toprows * XS 893 894 # set size so that at least 2//3 of a card is visible with 12 cards 895 h1 = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 896 h2 = (3 + reserves // decks) * YS 897 h = max(h1, h2) 898 899 # create foundations 900 x, y = w - XS * decks, YM 901 for i in range(decks): 902 for rank in range(ranks): 903 self.s.foundations.append(S(x, y, suit=rank)) 904 y += YS 905 x, y = x + XS, YM 906 907 # create rows 908 x, y = XM, YM 909 for i in range(rows // 2): 910 self.s.rows.append(S(x, y)) 911 x += XS 912 x, y = XM, (YS + h) // 2 913 for i in range(rows // 2): 914 self.s.rows.append(S(x, y)) 915 x += XS 916 self.setRegion(self.s.rows, (0, 0, XS * rows // 2 + XM // 2, 999999)) 917 918 # create reserves 919 x, y = w - XS * decks, YM + YS * 4 920 for i in range(decks): 921 for i in range(reserves // decks): 922 self.s.reserves.append(S(x, y)) 923 y += YS 924 x, y = x + XS, YM + YS * 4 925 926 # create talon 927 x, y = XM, h 928 self.s.talon = s = S(x, y) 929 if texts: 930 # place text right of stack 931 self._setText(s, 'se') 932 933 # set window 934 self.size = (w, YM + YS + h) 935 936 # 937 # Oonsoo layout 938 # - top: talon & rows 939 # - left: reserves 940 # - center right: rows 941 # 942 943 def oonsooLayout(self, rows, reserves, texts=0, playcards=12, center=1): 944 S = self.__createStack 945 CH = self.CH 946 XM, YM = self.XM, self.YM 947 XS, YS = self.XS, self.YS 948 949 decks = self.game.gameinfo.decks 950 assert rows % 2 == 0 951 toprows = decks + rows // 2 952 w = XM * 2 + toprows * (XS + XM) 953 954 # set size so that at least 2//3 of a card is visible with 12 cards 955 h = CH * 2 // 3 + (playcards - 1) * self.YOFFSET 956 h = max(h, 2 * YS) 957 958 # create talon 959 x, y = XM, YM 960 self.s.talon = s = S(x, y) 961 if texts: 962 # place text below stack 963 self._setText(s, 's') 964 965 # create rows 966 x, y = XS + XM * 3, YM 967 for i in range(rows // 2): 968 self.s.rows.append(S(x, y)) 969 x += XS + XM 970 x, y = XS + XM * 3, (YS + h) // 2 971 for i in range(rows // 2): 972 self.s.rows.append(S(x, y)) 973 x += XS + XM 974 self.setRegion(self.s.rows, (XS + XM, -999, 999999, 999999)) 975 976 # create reserves 977 x, y = XM, YM + YS + self.TEXT_HEIGHT 978 for i in range(decks): 979 for i in range(reserves // decks): 980 self.s.reserves.append(S(x, y)) 981 y += YS 982 x, y = x + XS, YM + YS * 4 983 984 # set window 985 self.size = (w, YM + YS + h) 986 987 # 988 # Ghulam layout 989 # - left & right: foundations & reserves 990 # - center: two groups of rows 991 # - lower right: talon 992 # 993 994 def ghulamLayout(self, rows, reserves=0, texts=0): 995 S = self.__createStack 996 XM, YM = self.XM, self.YM 997 XS, YS = self.XS, self.YS 998 999 suits = len(self.game.gameinfo.suits) 1000 assert rows % 2 == 0 1001 assert reserves % 2 == 0 1002 1003 # set size 1004 w, h = XM * 3 + XS * ((rows // 2) + 2), YM + YS * ((suits // 2) + 2) 1005 1006 # create foundations 1007 x, y = XM, YM 1008 for i in range(suits): 1009 self.s.foundations.append(S(x, y, suit=i)) 1010 y += YS 1011 if i == suits // 2 - 1: 1012 x, y = w - XS, YM 1013 1014 # create rows 1015 x = XM * 2 + XS 1016 for i in range(rows // 2): 1017 self.s.rows.append(S(x + i * XS, YM)) 1018 for i in range(rows // 2): 1019 self.s.rows.append(S(x + i * XS, h // 2)) 1020 self.setRegion(self.s.rows, (XM + XS, -999, w - XM - XS, 999999)) 1021 1022 # create reserves 1023 for i in range(reserves // 2): 1024 self.s.reserves.append(S(XM, h - YS * (i + 1))) 1025 for i in range(reserves // 2): 1026 self.s.reserves.append(S(w - XS, h - YS * (i + 1))) 1027 1028 # create talon 1029 self.s.talon = S(w - XS * 2, h - YS) 1030 if texts: 1031 assert 0 1032 1033 # set window 1034 self.size = (w, h) 1035 1036 # 1037 # Generiklon layout 1038 # - top: talon & foundations 1039 # - bottom: rows 1040 # 1041 1042 def generiklonLayout(self, rows, waste=1, height=6): 1043 S = self.__createStack 1044 XM, YM = self.XM, self.YM 1045 XS, YS = self.XS, self.YS 1046 1047 decks = self.game.gameinfo.decks 1048 suits = len(self.game.gameinfo.suits) + bool(self.game.gameinfo.trumps) 1049 frows = suits * decks // 2 1050 fspace = XS * (rows - 1) // 2 1051 1052 # Set window size 1053 w, h = XM + XS * rows, YM * 2 + YS * height 1054 self.size = (w, h) 1055 1056 # Talon 1057 x, y = XM, YM 1058 self.s.talon = s = S(x, y) 1059 self._setText(s, 'se') 1060 self.s.waste = s = S(x, y + YS) 1061 self._setText(s, 'se') 1062 1063 # Create foundations 1064 x = w - fspace - XS * frows // 2 1065 for suit in range(suits // 2): 1066 for i in range(decks): 1067 self.s.foundations.append(S(x, y, suit=suit)) 1068 x += XS 1069 x = w - fspace - XS * frows // 2 1070 y += YS 1071 for suit in range(suits // 2): 1072 for i in range(decks): 1073 self.s.foundations.append(S(x, y, suit=(suit + suits // 20))) 1074 x += XS 1075 1076 # bottom 1077 x, y = XM, YM * 2 + YS * 2 1078 for i in range(rows): 1079 self.s.rows.append(S(x, y)) 1080 x += XS 1081 self.setRegion(self.s.rows, (-999, y - YM, 999999, 999999)) 1082