home *** CD-ROM | disk | FTP | other *** search
/ Czech Logic, Card & Gambling Games / Logické hry.iso / hry / Sokoban / source / SOKOBAN.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2005-09-26  |  69.2 KB  |  2,801 lines

  1. /*
  2. (C) Petr LaÜtoviΦka
  3. */
  4. // je pot°eba p°ilinkovat comctl32.lib
  5.  
  6. //---------------------------------------------------------------------------
  7. #include <windows.h>
  8. #pragma hdrstop
  9. #include <commctrl.h>
  10. #include <stdio.h>
  11. #include <assert.h>
  12. #include <stdlib.h>
  13. #include "level.h"
  14. /*
  15. //---------------------------------------------------------------------------
  16. USERC("sokoban.rc");
  17. USEUNIT("level.cpp");
  18. USEUNIT("think.cpp");
  19. USEUNIT("lang.cpp");
  20. //---------------------------------------------------------------------------
  21. */
  22. const int minWidth=385; //minimßlnφ Üφ°ka okna
  23. //---------------------------------------------------------------------------
  24. int
  25.  level,         //Φφslo mφstnosti (poΦφtß se od 0)
  26.  moves,pushes,  //kroky,p°esuny
  27.  moverDirect,   //do jakΘho sm∞ru je hrßΦ natoΦen
  28.  playtime,      //Φas hry v sekundßch
  29.  width=1,       //Üφ°ka hracφ plochy (poΦet polφΦek)
  30.  height=1,      //v²Üka hracφ plochy (poΦet polφΦek)
  31.  x0,y0,         //sou°adnice levΘho hornφho rohu hracφ plochy uvnit° okna
  32.  bmH,bmW,       //rozm∞ry polφΦka na obrazovce
  33.  bfH,bfW,       //rozm∞ry polφΦka v BMP souboru
  34.  statusH=18,    //v²Üka stavovΘho °ßdku
  35.  toolBarH=28,   //v²Üka panelu nßstroj∙
  36.  moveDelay=30,  //zpo₧d∞nφ p°i pohybu hrßΦe
  37.  playTimer=250, //ΦasovaΦ p°i p°ehrßvßnφ °eÜenφ
  38.  fastTimer=20,  //ΦasovaΦ p°i zrychlenΘm p°ehrßvßnφ
  39.  titleTimer=7000,//za jak dlouho zmizφ informace z titulku okna
  40.  toolBarVisible=1,//zobrazuje se panel nßstroj∙
  41.  statusBarVisible=1,//zobrazuje se stavov² °ßdek
  42.  center=1,      //okno je uprost°ed obrazovky
  43.  nowalls=0,     //skrytφ zdφ
  44.  noobjects=0,   //skrytφ beden
  45.  noselected=0,  //vybranΘ objekty se nebudou zv²raz≥ovat
  46.  gratulOn=1,    //zobrazuje se zprßva p°i dosa₧enφ lepÜφho v²sledku
  47.  xtrans,ytrans, //zrcadlovΘ p°evrßcenφ
  48.  
  49.  Nlevels,       //poΦet mφstnostφ
  50.  prevLevel,     //p°edchozφ mφstnost
  51.  replay,        //po°adφ stiÜt∞nΘho tlaΦφtka na panelu p°ehrßvaΦe °eÜenφ
  52.  edQsaveMoverX, edQsaveMoverY,//umφst∞nφ hrßΦe v zapamatovanΘ mφstnosti
  53.  edQsaveWidth,edQsaveHeight, //rozm∞ry zapamotovanΘ mφstnosti
  54.  quickSaveLevel,//v kterΘ mφstnosti byla zapamatovßna pozice
  55.  quickLen,      //dΘlka pole quickSave
  56.  levDlgX=180,levDlgY=139,
  57.  levDlgW=576,levDlgH=389,       //rozm∞ry dialogu level∙
  58.  movError,      //nastavφ se p°i chybnΘm tahu, pot°eba p°i naΦφtßnφ °eÜenφ
  59.  clone,         //novß mφstnost vznikne kopiφ souΦasnΘ mφstnosti
  60.  check,         //provßdφ se kontrola levelu
  61.  notResize,     //zßkaz zm∞ny velikosti okna
  62.  notOptimize,   //zßkaz odstran∞nφ nedosa₧iteln²ch polφΦek
  63.  notMsg,        //zßkaz zobrazenφ chybov²ch hlßÜek
  64.  notdraw,       //zßkaz p°ekreslovßnφ okna
  65.  diroff[9],     //offsety na sousednφ polφΦka v hracφm poli - L,R,U,D,LU,RU,LD,RD,0
  66.  colWidth[]={50,110,20,110,58,76,270},
  67.  colOrder[]={0,1,2,3,4,5,6},
  68.  sortedCol=0,
  69.  descending=-1;
  70.  
  71. Pchar
  72.  logPos,        //pozice p°i zßpisu °eÜenφ
  73.  levels,levelsk,//databßze mφstnostφ po naΦtenφ do pam∞ti
  74.  user,userk;    //databßze u₧ivatelov²ch °eÜenφ
  75.  
  76. Psquare
  77.  mover,         //hrßΦ
  78.  selected,      //vybran² objekt
  79.  hilited,       //zv²razn∞n² objekt
  80.  editPos,       //poslednφ zm∞n∞nΘ polφΦko b∞hem jednoho pohybu myÜi
  81.  board=0,boardk;//hracφ pole
  82.  
  83. bool
  84.  delreg,        //smazßna konfigurace
  85.  editing,       //0=hra, 1=editace mφstnosti
  86.  rdonly,        //soubor sokoban.dat je pouze pro Φtenφ
  87.  ldown,rdown,   //je stiÜt∞no tlaΦφtko myÜi
  88.  bmWall,        //zdi se na sebe napojujφ
  89.  bmMover,       //zobrazuje se natoΦenφ hrßΦe
  90.  undoing,       //probφhß undo, poΦφtadlo krok∙ se sni₧uje
  91.  justSelected,  //p°i true je zakßzßno lineObj
  92.  stopTime,      //Φas je zastaven
  93.  modifData,     //databßze mφstnostφ byla zm∞n∞na
  94.  modifUser;     //u₧ivatelskß °eÜenφ byla zm∞n∞na
  95.  
  96. char
  97.  logbuf[32768], //buffer pro zßpis °eÜenφ
  98.  curAuthor[64]; //jmΘno u₧ivatele
  99.  
  100. UndoInfo rec[8192], *undoPos,*redoPos;//undo, redo
  101. QuickInfo *quickSave;//zapamatovanß pozice
  102. EdUndo edRec[1024], *edUndo,*edRedo; //undo p°i editaci
  103. Psquare1 edQsave;//zapamatovßnφ pozice p°i editaci
  104. Level *levoff;  //informace o vÜech mφstnostech
  105. static Level **A; //po°adφ v listBoxu
  106. //---------------------------------------------------------------------------
  107. HBITMAP bm;
  108. HDC dc,bmpdc;
  109. HWND hWin,hStatus,toolbar,toolPlay;
  110. HINSTANCE inst;
  111. HACCEL haccel;
  112.  
  113. TfileName
  114.  fnsave,
  115.  fnskin,
  116.  fndata,
  117.  fnuser,
  118.  fnhelp,
  119.  fnlevel;
  120.  
  121. char *title="Sokoban", *editTitle="Sokoban - Editor";
  122. const char *subkey="Software\\Petr Lastovicka\\sokoban";
  123. struct Treg { char *s; int *i; } regVal[]={
  124.  {"level",&level},
  125.  {"toolVis",&toolBarVisible},
  126.  {"statusVis",&statusBarVisible},
  127.  {"moveDelay",&moveDelay},
  128.  {"fastTimer",&fastTimer},
  129.  {"playTimer",&playTimer},
  130.  {"center",¢er},
  131.  {"nowalls",&nowalls},
  132.  {"noobjects",&noobjects},
  133.  {"noselect",&noselected},
  134.  {"gratul",&gratulOn},
  135.  {"levDlgX",&levDlgX},
  136.  {"levDlgY",&levDlgY},
  137.  {"levDlgW",&levDlgW},
  138.  {"levDlgH",&levDlgH},
  139.  {"colLev",&colWidth[0]},
  140.  {"colBest",&colWidth[1]},
  141.  {"colUser",&colWidth[3]},
  142.  {"colObj",&colWidth[4]},
  143.  {"colDim",&colWidth[5]},
  144.  {"colAuth",&colWidth[6]},
  145. };
  146. struct Tregs { char *s; char *i; DWORD n; } regValS[]={
  147.  {"skin",fnskin,sizeof(fnskin)},
  148.  {"save",fnsave,sizeof(fnsave)},
  149.  {"data",fndata,sizeof(fndata)},
  150.  {"user",fnuser,sizeof(fnuser)},
  151.  {"author",curAuthor,sizeof(curAuthor)},
  152.  {"extLevel",fnlevel,sizeof(fnlevel)},
  153.  {"language",lang,sizeof(lang)},
  154. };
  155.  
  156. OPENFILENAME savOfn={
  157.  sizeof(OPENFILENAME),0, 0,0,0,0,1,
  158.  fnsave, sizeof(fnsave),
  159.  0,0,0, 0, 0,0,0, "SAV", 0,0,0
  160. };
  161. OPENFILENAME userOfn={
  162.  sizeof(OPENFILENAME),0, 0,0,0,0,1,
  163.  fnuser, sizeof(fnuser),
  164.  0,0,0, 0, 0,0,0, "REC", 0,0,0
  165. };
  166. OPENFILENAME skinOfn={
  167.  sizeof(OPENFILENAME),0, 0,0,0,0,1,
  168.  fnskin, sizeof(fnskin),
  169.  0,0,0, 0, 0,0,0, "BMP", 0,0,0
  170. };
  171. OPENFILENAME dataOfn={
  172.  sizeof(OPENFILENAME),0, 0,0,0,0,1,
  173.  fndata, sizeof(fndata),
  174.  0,0,0, 0, 0,0,0, 0, 0,0,0
  175. };
  176. OPENFILENAME levelOfn={
  177.  sizeof(OPENFILENAME),0, 0,0,0,0,1,
  178.  fnlevel, sizeof(fnlevel),
  179.  0,0,0, 0, 0,0,0, "XSB", 0,0,0
  180. };
  181. //---------------------------------------------------------------------------
  182. int vmsg(char *caption, char *text, int btn, va_list v)
  183. {
  184.  char buf[256];
  185.  if(!text || notMsg) return IDCANCEL;
  186.  _vsnprintf(buf,sizeof(buf),text,v);
  187.  buf[sizeof(buf)-1]=0;
  188.  return MessageBox(hWin,buf,caption,btn);
  189. }
  190.  
  191. int msg3(int btn, char *caption, char *text, ...)
  192. {
  193.  va_list ap;
  194.  va_start(ap,text);
  195.  int result = vmsg(caption,text,btn,ap);
  196.  va_end(ap);
  197.  return result;
  198. }
  199.  
  200. void msg2(char *caption, char *text, ...)
  201. {
  202.  va_list ap;
  203.  va_start(ap,text);
  204.  vmsg(caption,text,MB_OK|MB_ICONERROR,ap);
  205.  va_end(ap);
  206. }
  207.  
  208. int msg1(int btn, char *text, ...)
  209. {
  210.  va_list ap;
  211.  va_start(ap,text);
  212.  int result = vmsg(title,text,btn,ap);
  213.  va_end(ap);
  214.  return result;
  215. }
  216.  
  217. void msg(char *text, ...)
  218. {
  219.  va_list ap;
  220.  va_start(ap,text);
  221.  vmsg(title,text,MB_OK|MB_ICONERROR,ap);
  222.  va_end(ap);
  223. }
  224.  
  225. void Sleep2(int ms)
  226. {
  227.  static int d=0;
  228.  
  229.  if(ms<20){
  230.    d+=ms;
  231.    if(d>20){
  232.      Sleep(20);
  233.      d-=20;
  234.    }
  235.  }else{
  236.    Sleep(ms);
  237.  }
  238. }
  239.  
  240. //pauza p°i pohybu hrßΦe
  241. void sleep()
  242. {
  243.  if(notdraw) return;
  244.  if(!replay){
  245.   MSG mesg;
  246.   if(PeekMessage(&mesg, NULL, WM_TIMER,WM_TIMER, PM_REMOVE)){
  247.     DispatchMessage(&mesg);
  248.   }
  249.  }
  250.  aminmax(moveDelay,0,500);
  251.  Sleep2(moveDelay);
  252. }
  253.  
  254. //zm∞na velikosti okna
  255. void setWindowXY(HWND hWnd,int x,int y)
  256. {
  257.  SetWindowPos(hWnd,0,x,y,0,0,SWP_NOSIZE|SWP_NOZORDER);
  258. }
  259.  
  260. int subBoard(Psquare p)
  261. {
  262.  if(!p) return -1;
  263.  return int(p-board);
  264. }
  265.  
  266. Psquare addBoard(int d)
  267. {
  268.  if(d<0) return 0;
  269.  return board+d;
  270. }
  271.  
  272. Psquare square(int x,int y)
  273. {
  274.  if(x<0 || y<0 || x>=width || y>=height) return 0;
  275.  return board + y*width + x;
  276. }
  277.  
  278. Psquare SquareXY(int x,int y)
  279. {
  280.  x= (x-x0)/bmW;
  281.  y= (y-y0)/bmH;
  282.  if(xtrans) x= width-1-x;
  283.  if(ytrans) y= height-1-y;
  284.  return square(x,y);
  285. }
  286. //---------------------------------------------------------------------------
  287. int getRadioButton(HWND hWnd, int item1, int item2)
  288. {
  289.  for(int i=item1; i<=item2; i++){
  290.    if(IsDlgButtonChecked(hWnd,i)){
  291.      return i-item1;
  292.    }
  293.  }
  294.  return 0;
  295. }
  296. //---------------------------------------------------------------------------
  297. //zobrazenφ doΦasnΘ informace v titulku okna
  298. void setTitle(char *txt)
  299. {
  300.  char buf[256],*s;
  301.  
  302.  strcpy(buf, editing ? editTitle : title);
  303.  if(txt && *txt){
  304.   strcat(buf," - ");
  305.   s=strchr(txt,0);
  306.   while(s>=txt && *s!='\\') s--;
  307.   strcat(buf,s+1);
  308.   s= strchr(buf,0);
  309.   while(s>=buf && *s!='.') s--;
  310.   if(*s=='.') *s=0;
  311.   SetTimer(hWin,300,titleTimer,0);
  312.  }
  313.  SetWindowText(hWin,buf);
  314. }
  315. //---------------------------------------------------------------------------
  316. //zm∞na textu na stavovΘm °ßdku
  317. void status(int i, char *txt, ...)
  318. {
  319.  char buf[256];
  320.  
  321.  if(notdraw) return;
  322.  va_list ap;
  323.  va_start(ap,txt);
  324.  vsprintf(buf,txt,ap);
  325.  SendMessage(hStatus, SB_SETTEXT, i, (LPARAM) buf);
  326.  va_end(ap);
  327. }
  328. //---------------------------------------------------------------------------
  329. void statusMoves()
  330. {
  331.  status(1,lng(550,"Moves:%d"),moves);
  332.  status(2,lng(551,"Pushes:%d"),pushes);
  333. }
  334. //---------------------------------------------------------------------------
  335. //aktualizace stavovΘho °ßdku
  336. void status()
  337. {
  338.  statusMoves();
  339.  status(0,"%d",level+1);
  340.  Level *lev= &levoff[level];
  341.  status(3, "%d - %d", lev->best.Mmoves, lev->best.Mpushes);
  342.  status(6,lev->author);
  343. }
  344. //---------------------------------------------------------------------------
  345. //p°ekreslenφ polφΦka
  346. void paintSquare(Psquare p)
  347. {
  348.  int i,x,y,dx,dy,k,k2;
  349.  static int T[]={BM_WALL+1,BM_WALL+3,BM_WALL+4,BM_WALL+2,
  350.    BM_WALL+1,BM_WALL+3,BM_WALL+4,BM_WALL};
  351.  static int D[4][3]={ {0,2,4},{1,2,5},{0,3,6},{1,3,7} };
  352.  
  353.  if(notdraw || !p) return;
  354.  x=p->x; y=p->y;
  355.  if(xtrans) x= width-1-x;
  356.  if(ytrans) y= height-1-y;
  357.  x= x*bmW + x0;
  358.  y= y*bmH + y0;
  359.  if(p->obj==BM_WALL && bmWall && !nowalls && p!=mover){
  360.   //napojenφ zdi na sousednφ ze∩
  361.   int bmW2= bmW>>1, bmH2= bmH>>1, bmW3= bmW-bmW2, bmH3= bmH-bmH2;
  362.   for(k=0; k<4; k++){
  363.     i=0;
  364.     k2=k;
  365.     if(xtrans) k2^=1;
  366.     if(ytrans) k2^=2;
  367.     if(nxtP(p,D[k2][0])->obj==BM_WALL) i+=1;
  368.     if(nxtP(p,D[k2][1])->obj==BM_WALL) i+=2;
  369.     if(nxtP(p,D[k2][2])->obj==BM_WALL) i+=4;
  370.     dx=dy=0;
  371.     if(k&1) dx=bmW2;
  372.     if(k>1) dy=bmH2;
  373.     BitBlt(dc, x+dx,y+dy, k&1 ? bmW3:bmW2, k>1 ? bmH3:bmH2,
  374.       bmpdc, T[i]*bmW+dx,dy, SRCCOPY);
  375.   }
  376.  }else{
  377.   if(p==mover){
  378.     i=BM_MOVER;
  379.     if(p->store) i=BM_MOVERSTORE;
  380.     if(bmMover){
  381.       i+= moverDirect;
  382.       if(xtrans && moverDirect<2 || ytrans && moverDirect>=2) i^=1;
  383.     }
  384.   }else{
  385.     i= p->obj;
  386.     if(p->store){
  387.       if(i==BM_GROUND) i=BM_STORE;
  388.       if(i==BM_OBJECT) i=BM_OBJECTSTORE;
  389.     }
  390.     if(i==BM_WALL && nowalls && !editing) i=BM_BACKGROUND;
  391.     if(i==BM_OBJECT && noobjects && !editing &&
  392.       (abs(p->x - mover->x)>1 || abs(p->y - mover->y)>1)){
  393.       i=BM_GROUND;
  394.     }
  395.     if(p==selected && !noselected){
  396.       if(i==BM_OBJECT) i=BM_HILITE;
  397.       if(i==BM_OBJECTSTORE) i=BM_HILITESTORE;
  398.     }
  399.   }
  400.   BitBlt(dc, x,y, bmW, bmH, bmpdc, i*bmW,0, SRCCOPY);
  401.  }
  402. }
  403. //---------------------------------------------------------------------------
  404. void paintSquare9(Psquare pos)
  405. {
  406.  for(int i=0; i<9; i++){
  407.    paintSquare(nxtP(pos,i));
  408.  }
  409. }
  410.  
  411. void paintSquareO(Psquare pos)
  412. {
  413.  if(noobjects) paintSquare9(pos);
  414.  else paintSquare(pos);
  415. }
  416. //---------------------------------------------------------------------------
  417. //vyber a zv²razni bednu
  418. void setSelected(Psquare p)
  419. {
  420.  if(p!=hilited && (!p || p->obj==BM_OBJECT)){
  421.    Psquare old=hilited;
  422.    hilited=selected=p;
  423.    paintSquare(old);
  424.    paintSquare(p);
  425.  }
  426. }
  427. //---------------------------------------------------------------------------
  428. //klientskß Φßst okna bez toolBaru a statusBaru
  429. void getClient(RECT *rc)
  430. {
  431.  GetClientRect(hWin,rc);
  432.  if(toolBarVisible || replay) rc->top+= toolBarH;
  433.  if(statusBarVisible) rc->bottom-= statusH;
  434. }
  435. //---------------------------------------------------------------------------
  436. //p°ekreslenφ hracφ plochy
  437. void repaint()
  438. {
  439.  RECT rc;
  440.  
  441.  getClient(&rc);
  442.  HRGN hrgn=CreateRectRgnIndirect(&rc);
  443.  SelectClipRgn(dc,hrgn);
  444.  DeleteObject(hrgn);
  445.  
  446.  int xk= x0+width*bmW;
  447.  int yk= y0+height*bmH;
  448.  
  449.  rc.top+= (y0-rc.top)%bmH -bmH;
  450.  rc.left+= (x0-rc.left)%bmW -bmW;
  451.  for(int y=rc.top; y<rc.bottom; y+=bmH){
  452.   for(int x=rc.left; x<rc.right; x+=bmW){
  453.    if(x<x0 || x+bmW>xk || y<y0 || y+bmH>yk)
  454.      BitBlt(dc, x, y, bmW, bmH, bmpdc, bmW*BM_BACKGROUND,0, SRCCOPY);
  455.   }
  456.  }
  457.  for(Psquare p=board; p<boardk; p++) paintSquare(p);
  458.  SelectClipRgn(dc,0);
  459. }
  460. //---------------------------------------------------------------------------
  461. //p°ekreslenφ celΘho okna
  462. void update()
  463. {
  464.  repaint();
  465.  status();
  466. }
  467. //---------------------------------------------------------------------------
  468. //zm∞na velikosti okna
  469. void resize()
  470. {
  471.  int w,h,bw,bh,wc,hc,x,y,panelsH;
  472.  RECT rc;
  473.  
  474.  if(notResize) return;
  475.  notResize++;
  476.  
  477.  SystemParametersInfo(SPI_GETWORKAREA,0,&rc,0);
  478.  panelsH=0;
  479.  if(toolBarVisible || replay) panelsH+=toolBarH;
  480.  y0=panelsH;
  481.  if(statusBarVisible) panelsH+= statusH;
  482.  wc = 2*GetSystemMetrics(SM_CXFIXEDFRAME);
  483.  hc = panelsH + 2*GetSystemMetrics(SM_CYFIXEDFRAME)
  484.    + GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION);
  485.  //zmenÜi velikost polφΦek tak, aby se okno veÜlo na obrazovku
  486.  bw= (rc.right-rc.left-wc)/width;
  487.  bh= (rc.bottom-rc.top-hc)/height;
  488.  aminmax(bw,4,bfW);
  489.  aminmax(bh,4,bfH);
  490.  //musφ b²t zachovßn pom∞r v²Üka:Üφ°ka
  491.  if(bfW*bh > bfH*bw){
  492.    bh=bw*bfH/bfW;
  493.  }else{
  494.    bw=bh*bfW/bfH;
  495.  }
  496.  //prvnφ °ßdek obsahuje zmenÜenß polφΦka, v druhΘm °ßdku jsou p∙vodnφ
  497.  for(int i=0; i<BM_NUM; i++){
  498.    StretchBlt(bmpdc,i*bw,0,bw,bh,bmpdc,i*bfW,bfH,bfW,bfH,SRCCOPY);
  499.  }
  500.  bmW=bw;
  501.  bmH=bh;
  502.  //nastav rozm∞ry a polohu okna
  503.  w = wc + max(width*bmW, minWidth*GetDeviceCaps(dc,LOGPIXELSX)/96);
  504.  h = hc + height*bmH;
  505.  x= (rc.right + rc.left - w)/2;
  506.  y= (rc.top + rc.bottom - h)/2;
  507.  if(!IsZoomed(hWin)){
  508.   SetWindowPos(hWin,0, x,y, w,h,
  509.     (center ? 0:SWP_NOMOVE)|SWP_NOZORDER|SWP_NOCOPYBITS);
  510.  }
  511.  //spoΦti sou°adnice levΘho hornφho rohu hracφho pole
  512.  getClient(&rc);
  513.  x0= (rc.right-rc.left-width*bmW)/2;
  514.  y0+= (rc.bottom-rc.top-height*bmH)/2;
  515.  //oprav velikosti panel∙ nßstroj∙
  516.  SendMessage(hStatus,WM_SIZE,0,0);
  517.  SendMessage(toolbar, TB_AUTOSIZE, 0,0);
  518.  SendMessage(toolPlay, TB_AUTOSIZE, 0,0);
  519.  //p°ekresli okno
  520.  InvalidateRect(hWin,&rc,0);
  521.  notResize--;
  522. }
  523. //---------------------------------------------------------------------------
  524. void squareCopy(HDC srcDC, int destI, int srcI)
  525. {
  526.  BitBlt(bmpdc,bfW*destI,bfH, bfW,bfH, srcDC,bfW*srcI,0, SRCCOPY);
  527. }
  528. //---------------------------------------------------------------------------
  529. //smφchßnφ dvou obrßzk∙
  530. void squareMask(HDC srcDC, int destI, int srcI, int srcG)
  531. {
  532.  int x,y;
  533.  
  534.  squareCopy(srcDC, destI, srcG);
  535.  srcI *= bfW;
  536.  destI *= bfW;
  537.  COLORREF transp= GetPixel(srcDC, srcI, 0);
  538.  
  539.  for(x=0; x<bfW; x++)
  540.   for(y=0; y<bfH; y++){
  541.    COLORREF c= GetPixel(srcDC, x+srcI, y);
  542.    if(c!=transp) SetPixel(bmpdc, x+destI, y+bfH, c);
  543.   }
  544. }
  545. //---------------------------------------------------------------------------
  546. //naΦtenφ vzhledu z bitmapy
  547. char *loadSkin(HBITMAP fbmp)
  548. {
  549.  static int D[]={F_NUM+7,F_NUM+4,F_NUM+3,F_NUM};
  550.  int i,w,h,typ,bmW1;
  551.  BITMAP bmp;
  552.  char *errs= lng(700,"Cannot create bitmap for skin");
  553.  
  554.  if(fbmp){
  555.   GetObject(fbmp,sizeof(BITMAP),&bmp);
  556.   w= bmp.bmWidth;
  557.   h= bmp.bmHeight;
  558.   //zjiÜt∞nφ typu,  hrßΦ 1x/4x,  ze∩ 1x/5x
  559.   for(typ=0; typ<sizeA(D); typ++){
  560.     if(w%D[typ]==0 && w/D[typ]>=h) break;
  561.   }
  562.   if(typ==sizeA(D)){
  563.    for(typ=0; typ<sizeA(D); typ++){
  564.      if(w%D[typ]==0) break;
  565.    }
  566.   }
  567.   if(typ==sizeA(D)){
  568.     errs=lng(701,"Invalid bitmap width");
  569.   }else{
  570.    bmW1= w/D[typ];
  571.    if(bmW1<9 || h<9){
  572.      errs=lng(701,"Invalid bitmap width");
  573.    }else{
  574.     HDC fdc= CreateCompatibleDC(dc);
  575.     if(fdc){
  576.      HGDIOBJ oldB= SelectObject(fdc, fbmp);
  577.      HBITMAP bm1= CreateCompatibleBitmap(dc, bmW1*BM_NUM, 2*h);
  578.      if(bm1){
  579.       DeleteObject(SelectObject(bmpdc,bm1));
  580.       bmWall= typ<2;
  581.       bmMover= (typ&1)==0;
  582.       bfW=bmW1;
  583.       bfH=h;
  584.       squareCopy(fdc,BM_GROUND,F_GROUND);
  585.       squareCopy(fdc,BM_BACKGROUND,F_BACKGROUND);
  586.       squareCopy(fdc,BM_STORE,F_STORE);
  587.       squareMask(fdc,BM_OBJECT,F_OBJECT,F_GROUND);
  588.       squareMask(fdc,BM_OBJECTSTORE,F_OBJECT,F_STORE);
  589.       squareMask(fdc,BM_HILITE,F_HILITE,F_GROUND);
  590.       squareMask(fdc,BM_HILITESTORE,F_HILITE,F_STORE);
  591.       for(i=0; i<4; i++){
  592.         squareMask(fdc, i+BM_MOVER, i+F_MOVER,F_GROUND);
  593.         squareMask(fdc, i+BM_MOVERSTORE, i+F_MOVER,F_STORE);
  594.         if(!bmMover) break;
  595.       }
  596.       int f_w=F_WALL;
  597.       if(bmMover) f_w+=3;
  598.       for(i=0; i<5; i++){
  599.         squareCopy(fdc, i+BM_WALL, i+f_w);
  600.         if(!bmWall) break;
  601.       }
  602.       bm=bm1;
  603.       resize();
  604.       errs=0;
  605.      }
  606.      DeleteObject(SelectObject(fdc, oldB));
  607.      DeleteDC(fdc);
  608.     }
  609.    }
  610.   }
  611.  }
  612.  return errs;
  613. }
  614. //---------------------------------------------------------------------------
  615. //naΦtenφ vzhledu ze souboru fnskin
  616. char *loadSkin()
  617. {
  618.  BITMAPFILEHEADER hdr;
  619.  HANDLE h;
  620.  DWORD r,s;
  621.  char *errs= lng(702,"Cannot load BMP file");
  622.  
  623.  h= CreateFile(fnskin, GENERIC_READ,FILE_SHARE_READ,
  624.    0,OPEN_EXISTING,0,0);
  625.  if(h!=INVALID_HANDLE_VALUE){
  626.   ReadFile(h,&hdr,sizeof(BITMAPFILEHEADER),&r,0);
  627.   if(r==sizeof(BITMAPFILEHEADER) && ((char*)&hdr.bfType)[0]=='B' && ((char*)&hdr.bfType)[1]=='M'){
  628.    s = GetFileSize(h,0) - sizeof(BITMAPFILEHEADER);
  629.    char *b= new char[s];
  630.    if(b){                     
  631.     ReadFile(h,b,s,&r,0);
  632.     if(r==s){
  633.      BITMAPINFOHEADER* info = (BITMAPINFOHEADER*)b;
  634.      HBITMAP fbmp = CreateDIBitmap(dc, info, CBM_INIT,
  635.       b+hdr.bfOffBits-sizeof(BITMAPFILEHEADER),
  636.       (BITMAPINFO*)b, DIB_RGB_COLORS);
  637.      errs= loadSkin(fbmp);
  638.      if(errs){
  639.       DeleteObject(fbmp);
  640.      }else{
  641.       setTitle(fnskin);
  642.      }
  643.     }
  644.     delete[] b;
  645.    }
  646.   }
  647.   CloseHandle(h);
  648.  }
  649.  return errs;
  650. }
  651. //---------------------------------------------------------------------------
  652. int openFileDlg(OPENFILENAME *o)
  653. {
  654.  for(;;){
  655.    o->hwndOwner= hWin;
  656.    o->Flags= OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_READONLY;
  657.    if(GetOpenFileName(o)) return 1; //ok
  658.    if(CommDlgExtendedError()!=FNERR_INVALIDFILENAME
  659.      || !*o->lpstrFile) return 0; //cancel
  660.    *o->lpstrFile=0;
  661.  }
  662. }
  663. //---------------------------------------------------------------------------
  664. int saveFileDlg(OPENFILENAME *o, bool prompt)
  665. {
  666.  for(;;){
  667.    o->hwndOwner= hWin;
  668.    o->Flags= OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
  669.    if(prompt) o->Flags|= OFN_OVERWRITEPROMPT;
  670.    if(GetSaveFileName(o)) return 1; //ok
  671.    if(CommDlgExtendedError()!=FNERR_INVALIDFILENAME
  672.      || !*o->lpstrFile) return 0; //cancel
  673.    *o->lpstrFile=0;
  674.  }
  675. }
  676. //---------------------------------------------------------------------------
  677. void openSkin()
  678. {
  679.  if(openFileDlg(&skinOfn)){
  680.    char *err= loadSkin();
  681.    msg(err);
  682.  }
  683. }
  684. //---------------------------------------------------------------------------
  685. char *onlyExt(char *dest)
  686. {
  687.  char *s;
  688.  
  689.  if(!*fnskin) strcpy(fnskin,"skins\\");
  690.  strcpy(dest, fnskin);
  691.  for(s=strchr(dest,0); s>=dest && *s!='\\'; s--) ;
  692.  s++;
  693.  strcpy(s,"*.bmp");
  694.  return fnskin + (s-dest);
  695. }
  696. //---------------------------------------------------------------------------
  697. void prevSkin()
  698. {
  699.  WIN32_FIND_DATA fd;
  700.  TfileName buf,prev;
  701.  HANDLE h;
  702.  char *t;
  703.  int pass=0;
  704.  
  705.  t= onlyExt(buf);
  706.  h= FindFirstFile(buf,&fd);
  707.  if(h==INVALID_HANDLE_VALUE){
  708.   openSkin();
  709.  }else{
  710.   *prev=0;
  711.   do{
  712.     if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
  713.       if(!_stricmp(t,fd.cFileName) || pass>10){
  714.         strcpy(t, prev);
  715.         if(!loadSkin()) break;
  716.       }else{
  717.         strcpy(prev, fd.cFileName);
  718.       }
  719.     }
  720.     if(!FindNextFile(h,&fd)){
  721.       FindClose(h);
  722.       h = FindFirstFile(buf,&fd);
  723.       pass++;
  724.     }
  725.   }while(pass<20);
  726.   FindClose(h);
  727.  }
  728. }
  729. //---------------------------------------------------------------------------
  730. void nextSkin()
  731. {
  732.  WIN32_FIND_DATA fd;
  733.  TfileName buf;
  734.  HANDLE h;
  735.  char *t;
  736.  int pass=0;
  737.  
  738.  t= onlyExt(buf);
  739.  h= FindFirstFile(buf,&fd);
  740.  if(h==INVALID_HANDLE_VALUE){
  741.   openSkin();
  742.  }else{
  743.   do{
  744.     if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
  745.       if(pass){
  746.        strcpy(t, fd.cFileName);
  747.        if(!loadSkin()) break;
  748.       }
  749.       else if(!_stricmp(t,fd.cFileName)) pass++;
  750.     }
  751.     if(!FindNextFile(h,&fd)){
  752.       FindClose(h);
  753.       h = FindFirstFile(buf,&fd);
  754.       pass++;
  755.     }
  756.   }while(pass<3);
  757.   FindClose(h);
  758.  }
  759. }
  760. //---------------------------------------------------------------------------
  761. //smazßnφ konfigurace
  762. void deleteini()
  763. {
  764.  HKEY key;
  765.  DWORD i;
  766.  
  767.  delreg=true;
  768.  if(RegDeleteKey(HKEY_CURRENT_USER, subkey)==ERROR_SUCCESS){
  769.   if(RegOpenKey(HKEY_CURRENT_USER,
  770.     "Software\\Petr Lastovicka",&key)==ERROR_SUCCESS){
  771.    i=1;
  772.    RegQueryInfoKey(key,0,0,0,&i,0,0,0,0,0,0,0);
  773.    RegCloseKey(key);
  774.    if(!i)
  775.     RegDeleteKey(HKEY_CURRENT_USER, "Software\\Petr Lastovicka");
  776.   }
  777.  }
  778. }
  779.  
  780. //ulo₧enφ konfigurace
  781. void writeini()
  782. {
  783.  HKEY key;
  784.  if(RegCreateKey(HKEY_CURRENT_USER, subkey, &key)!=ERROR_SUCCESS)
  785.    msg(lng(735,"Cannot write to Windows registry"));
  786.  else{
  787.   for(Treg *u=regVal; u<endA(regVal); u++){
  788.     RegSetValueEx(key, u->s, 0,REG_DWORD,
  789.       (BYTE *)u->i, sizeof(int));
  790.   }
  791.   for(Tregs *v=regValS; v<endA(regValS); v++){
  792.     RegSetValueEx(key, v->s, 0,REG_SZ,
  793.       (BYTE *)v->i, (DWORD) strlen(v->i)+1);
  794.   }
  795.   RegCloseKey(key);
  796.  }
  797. }
  798.  
  799. //naΦtenφ konfigurace
  800. void readini()
  801. {
  802.  HKEY key;
  803.  DWORD d;
  804.  if(RegOpenKey(HKEY_CURRENT_USER, subkey, &key)==ERROR_SUCCESS){
  805.   for(Treg *u=regVal; u<endA(regVal); u++){
  806.     d=sizeof(int);
  807.     RegQueryValueEx(key,u->s,0,0, (BYTE *)u->i, &d);
  808.   }
  809.   for(Tregs *v=regValS; v<endA(regValS); v++){
  810.     d=v->n;
  811.     RegQueryValueEx(key,v->s,0,0, (BYTE *)v->i, &d);
  812.   }
  813.   RegCloseKey(key);
  814.  }
  815. }
  816.  
  817. //ulo₧enφ konfigurace a ulo₧enφ databßze
  818. void saveAtExit()
  819. {
  820.  saveUser();
  821.  saveData();
  822.  if(!delreg) writeini();
  823. }
  824. //---------------------------------------------------------------------------
  825. //inicializace novΘ polo₧ky undo
  826. void prepareUndo()
  827. {
  828.  undoPos->mover=mover;
  829.  undoPos->pushBeg=0;
  830.  undoPos->mov=1;
  831.  undoPos->pus=0;
  832.  redoPos=0;
  833.  justSelected=false;
  834. }
  835. //---------------------------------------------------------------------------
  836. //zapiÜ znak do °eÜenφ
  837. void wrLog(char ch)
  838. {
  839.  if(!logPos || logPos>=endA(logbuf)) return;
  840.  if(logPos-2 >= logbuf && logPos[-1]==ch){
  841.    if(logPos[-2]==ch && (logPos-3<logbuf || logPos[-3]!='Z')){
  842.      logPos[-2]='C';
  843.      return;
  844.    }
  845.    if(logPos[-2]>='A' && logPos[-2]<'Z'){
  846.      logPos[-2]++;
  847.      return;
  848.    }
  849.  }
  850.  *logPos++ = ch;
  851. }
  852. //---------------------------------------------------------------------------
  853. //p°esun o jedno polφΦko ve sm∞ru direct
  854. bool move(int direct, bool setUndo=false)
  855. {
  856.  moverDirect=direct;
  857.  Psquare old=mover;
  858.  mover= nxtP(mover,direct);
  859.  Psquare next= nxtP(mover,direct);
  860.  if(mover->obj==BM_WALL || mover->obj==BM_OBJECT && next->obj!=BM_GROUND){
  861.    //narazil do zdi nebo do dvou beden
  862.    mover=old;
  863.    paintSquare(old); //zm∞≥ natoΦenφ hrßΦe
  864.    movError++;
  865.    return false;
  866.  }
  867.  //zapiÜ undo
  868.  if(setUndo){
  869.    prepareUndo();
  870.    undoPos->mover=old;
  871.    if(mover->obj!=BM_GROUND){
  872.      undoPos->pushBeg=mover;
  873.      undoPos->pushEnd=next;
  874.      undoPos->pus=1;
  875.    }
  876.    undoPos++;
  877.  }
  878.  paintSquareO(old); 
  879.  char ch= char(direct + '0');
  880.  if(mover->obj!=BM_GROUND){
  881.    //p°esun bedny
  882.    ch= char(direct + '4');
  883.    next->obj= BM_OBJECT;
  884.    mover->obj= BM_GROUND;
  885.    paintSquare(next);
  886.    pushes++;
  887.  }
  888.  if(undoing) moves--; else moves++;
  889.  wrLog(ch);
  890.  paintSquareO(mover);
  891.  //p°ekresli stavov² °ßdek
  892.  statusMoves();
  893.  return true;
  894. }
  895. //---------------------------------------------------------------------------
  896. //pr∙chod nejkratÜφ cestou, kterß byla nalezena funkcφ findDist
  897. void move1(Psquare dest)
  898. {
  899.  for(int i=0; i<4; i++){
  900.   Psquare p= nxtP(dest,i);
  901.   if(p->dist == dest->dist - 1){
  902.     if(p->dist!=0){
  903.       move1(p);
  904.       sleep();
  905.     }
  906.     move(i^1);
  907.     return;
  908.   }
  909.  }
  910. }
  911. //---------------------------------------------------------------------------
  912. //stejnΘ jako move1, ale pou₧φvß se p°i undo
  913. void move1R()
  914. {
  915.  int i;
  916.  Psquare p,dest;
  917.  
  918.  for(dest=mover; ;dest=p){
  919.   for(i=0; i<4; i++){
  920.     p= nxtP(dest,i);
  921.     if(p->dist == dest->dist - 1) break;
  922.   }
  923.   move(i);
  924.   if(p->dist==0) break;
  925.   sleep();
  926.  }
  927. }
  928. //---------------------------------------------------------------------------
  929. //p°esun hrßΦe na polφΦko dest nejkratÜφ cestou
  930. //kdy₧ cesta neexistuje, vrßtφ 0
  931. int move(Psquare dest)
  932. {
  933.  if(!dest || dest->obj!=BM_GROUND) return 0;
  934.  if(dest==mover) return 1;
  935.  findDist(mover,dest);
  936.  if(dest->distId!=distId) return 0;
  937.  move1(dest);
  938.  return 2;
  939. }
  940. //---------------------------------------------------------------------------
  941. //stejnΘ jako move, ale pou₧φvß se p°i undo
  942. int moveR(Psquare dest)
  943. {
  944.  if(!dest || dest->obj!=BM_GROUND) return 0;
  945.  if(dest==mover) return 1;
  946.  findDist(dest,mover);
  947.  if(mover->distId!=distId) return 0;
  948.  move1R();
  949.  return 2;
  950. }
  951. //---------------------------------------------------------------------------
  952. const int MAXEVAL=32767;
  953.  
  954. void push1(Psquare dest, int direct)
  955. {
  956.  Psquare pn,pnn;
  957.  
  958.  pn=nxtP(dest,direct);
  959.  if(dest->obj==BM_OBJECT){
  960.    if(move(pn)) return;
  961.  }
  962.  pnn=nxtP(pn,direct);
  963.  push1(pn,dest->pushDirect[direct]);
  964.  sleep();
  965.  if(pnn!=mover){
  966.    move(pnn);
  967.    sleep();
  968.  }
  969.  move(direct^1);
  970. }
  971. //---------------------------------------------------------------------------
  972. //tßhne bednu z dest na polφΦko sousedφcφ s mover
  973. //cesta u₧ musela b²t nalezena pomocφ movePush0
  974. //direct je sm∞r hrßΦe od dest
  975. void push1R(Psquare dest, int direct)
  976. {
  977.  Psquare pn;
  978.  int im;
  979.  
  980.  for(;;){
  981.    pn=nxtP(dest,direct);
  982.    im=dest->pushDirect[direct];
  983.    dest->obj=BM_GROUND;
  984.    mover=nxtP(pn,direct);
  985.    paintSquare(dest);
  986.    pn->obj=BM_OBJECT;
  987.    dest=pn;
  988.    moverDirect=im;
  989.    direct=im;
  990.    paintSquareO(mover);
  991.    paintSquareO(dest);
  992.    moves--;
  993.    pushes--;
  994.    statusMoves();
  995.    pn=nxtP(dest,im);
  996.    if(pn!=mover){
  997.      sleep();
  998.      moveR(pn);
  999.    }
  1000.    if(dest->pushDirect[direct]==9) break;
  1001.    sleep();
  1002.  }
  1003. }
  1004. //---------------------------------------------------------------------------
  1005. int movePush0(Psquare pBegin, Psquare pEnd)
  1006. {
  1007.  int i,im,e;
  1008.  short m;
  1009.  Psquare mover0,p,pn,pp,pobj;
  1010.  
  1011.  if(!(pEnd && pBegin && pEnd->obj==BM_GROUND && pBegin->obj==BM_OBJECT)){
  1012.    return -1;
  1013.  }
  1014.  mover0=mover;
  1015.  for(p=board; p<boardk; p++){
  1016.    p->cont[0]=p->cont[1]=p->cont[2]=p->cont[3]=MAXEVAL;
  1017.    p->finalDist=1;
  1018.  }
  1019.  for(i=0; i<4; i++){
  1020.    p= nxtP(pBegin,i);
  1021.    findDist(mover,p);
  1022.    if(p->distId==distId){
  1023.      pBegin->cont[i]= (short)p->dist;
  1024.      pBegin->pushDirect[i]=(char)9;
  1025.    }
  1026.  }
  1027.  pBegin->finalDist=0;
  1028.  pBegin->obj=BM_GROUND;
  1029.  for(;;){
  1030.    //najdi nefinßlnφ pozici s min. vzdßlenostφ
  1031.    m=MAXEVAL;
  1032.    for(p=board; p<boardk; p++){
  1033.      if(!p->finalDist){
  1034.        for(i=0; i<4; i++){
  1035.          if(p->cont[i]<m && p->cont[i]>=0){
  1036.            m=p->cont[i];
  1037.            im=i;
  1038.            pobj=p;
  1039.          }
  1040.        }
  1041.      }
  1042.    }
  1043.    if(m==MAXEVAL){
  1044.      pBegin->obj=BM_OBJECT;
  1045.      mover=mover0;
  1046.      return -1;
  1047.    }
  1048.    pobj->cont[im] -= (short)MAXEVAL;
  1049.    for(i=0; ; i++){
  1050.      if(pobj->cont[i]>=0 && pobj->cont[i]<MAXEVAL) break;
  1051.      if(i==3){ pobj->finalDist=1; break; }
  1052.    }
  1053.    if(pobj==pEnd) break;
  1054.    mover=nxtP(pobj,im);
  1055.    pobj->obj=BM_OBJECT;
  1056.    //sni₧ vzdßlenost sousednφch pozic
  1057.    for(i=0; i<4; i++){
  1058.      pn=prvP(pobj,i);
  1059.      if(pn->obj==BM_GROUND){
  1060.        pp=nxtP(pobj,i);
  1061.        findDist(mover,pp);
  1062.        if(pp->distId==distId){
  1063.          e=m+2+pp->dist;
  1064.          if(pn->cont[i]>e){
  1065.            pn->cont[i]=(short)e;
  1066.            pn->pushDirect[i]=(char)im;
  1067.            pn->finalDist=0;
  1068.          }
  1069.        }
  1070.      }
  1071.    }
  1072.    pobj->obj=BM_GROUND;
  1073.  }
  1074.  pBegin->obj=BM_OBJECT;
  1075.  mover=mover0;
  1076.  return im;
  1077. }
  1078. //---------------------------------------------------------------------------
  1079. bool movePush(Psquare pBegin, Psquare pEnd, bool setUndo=false)
  1080. {
  1081.  int im,mov0,pus0;
  1082.  
  1083.  im= movePush0(pBegin,pEnd);
  1084.  if(im>=0){
  1085.    if(setUndo){
  1086.      prepareUndo();
  1087.      undoPos->pushBeg= pBegin;
  1088.      undoPos->pushEnd= pEnd;
  1089.      mov0=moves;
  1090.      pus0=pushes;
  1091.    }
  1092.    push1(pEnd,im);
  1093.    if(setUndo){
  1094.      undoPos->mov= short(moves-mov0);
  1095.      undoPos->pus= short(pushes-pus0);
  1096.      undoPos++;
  1097.    }
  1098.    return true;
  1099.  }
  1100.  return false;
  1101. }
  1102. //---------------------------------------------------------------------------
  1103. bool movePushR(Psquare pBegin, Psquare pEnd)
  1104. {
  1105.  pEnd->obj=BM_GROUND;
  1106.  pBegin->obj=BM_OBJECT;
  1107.  int im= movePush0(pBegin,pEnd);
  1108.  pBegin->obj=BM_GROUND;
  1109.  pEnd->obj=BM_OBJECT;
  1110.  if(im>=0){
  1111.    push1R(pEnd,im);
  1112.    return true;
  1113.  }
  1114.  return false;
  1115. }
  1116. //---------------------------------------------------------------------------
  1117. //zjistφ sm∞r mezi pBegin a pEnd
  1118. //pokud nejsou ve stejnΘm °ßdku nebo sloupci, vracφ -1
  1119. int direction(Psquare pBegin, Psquare pEnd)
  1120. {
  1121.  int dir=-1;
  1122.  if(pEnd){
  1123.   if(pEnd->x==pBegin->x){
  1124.     if(pEnd->y > pBegin->y) dir=3;
  1125.     else dir=2;
  1126.   }else if(pEnd->y==pBegin->y){
  1127.     if(pEnd->x > pBegin->x) dir=1;
  1128.     else dir=0;
  1129.   }
  1130.  }
  1131.  return dir;
  1132. }
  1133. //---------------------------------------------------------------------------
  1134. //najde jednu bednu mezi hrßΦem a polφΦkem pEnd
  1135. Psquare lineObj(Psquare pEnd)
  1136. {
  1137.  Psquare p,pObj;
  1138.  
  1139.  if(pEnd && pEnd->obj==BM_GROUND){
  1140.   int dir=direction(mover,pEnd);
  1141.   if(dir>=0){
  1142.    int n=0;
  1143.    for(p=mover; p!=pEnd; p=nxtP(p,dir)){
  1144.      if(p->obj==BM_OBJECT){
  1145.        pObj=p;
  1146.        n++;
  1147.      }else if(p->obj!=BM_GROUND){
  1148.        return 0;
  1149.      }
  1150.    }
  1151.    if(n==1) return pObj;
  1152.   }
  1153.  }
  1154.  return 0;
  1155. }
  1156. //---------------------------------------------------------------------------
  1157. void setMover(Psquare pos)
  1158. {
  1159.  if(pos!=mover){
  1160.    Psquare old=mover;
  1161.    mover=pos;
  1162.    paintSquare(old);
  1163.  }
  1164. }
  1165. //---------------------------------------------------------------------------
  1166. void xchgEd()
  1167. {
  1168.  int x,y;
  1169.  char o;
  1170.  bool s;
  1171.  
  1172.  x= mover->x;
  1173.  y= mover->y;
  1174.  Psquare p= square(edUndo->x,edUndo->y);
  1175.  if(edUndo->obj==BM_MOVER){
  1176.    //p°esun hrßΦe
  1177.    setMover(p);
  1178.    edUndo->x= x;
  1179.    edUndo->y= y;
  1180.  }else if(edUndo->obj==BM_MOVER+1){
  1181.    //tlaΦenφ bedny
  1182.    Psquare next= nxtP(p,direction(mover,p));
  1183.    next->obj = BM_OBJECT;
  1184.    paintSquare(next);
  1185.    p->obj=BM_GROUND;
  1186.    setMover(p);
  1187.    edUndo->x= x;
  1188.    edUndo->y= y;
  1189.    edUndo->obj=BM_MOVER+2;
  1190.  }else if(edUndo->obj==BM_MOVER+2){
  1191.    //tßhnutφ bedny
  1192.    Psquare next= nxtP(mover,direction(p,mover));
  1193.    next->obj = BM_GROUND;
  1194.    paintSquare(next);
  1195.    mover->obj=BM_OBJECT;
  1196.    setMover(p);
  1197.    edUndo->x= x;
  1198.    edUndo->y= y;
  1199.    edUndo->obj=BM_MOVER+1;
  1200.  }else{
  1201.    o= p->obj;
  1202.    s= p->store;
  1203.    p->obj= edUndo->obj;
  1204.    p->store= edUndo->store;
  1205.    edUndo->obj=o;
  1206.    edUndo->store=s;
  1207.  }
  1208.  paintSquare9(p);
  1209. }
  1210. //---------------------------------------------------------------------------
  1211. //zp∞t
  1212. bool undo()
  1213. {
  1214.  if(editing){
  1215.    if(edUndo<=edRec) return false;
  1216.    if(!edRedo) edRedo=edUndo;
  1217.    edUndo--;
  1218.    xchgEd();
  1219.  }else{
  1220.   if(undoPos<=rec) return false;
  1221.   undoPos->mover=mover;
  1222.   if(!redoPos) redoPos=undoPos;
  1223.   undoPos--;
  1224.   Psquare pb=undoPos->pushBeg, pe=undoPos->pushEnd;
  1225.   undoing=true;
  1226.   if(notdraw){
  1227.     if(pb){
  1228.       pb->obj=BM_OBJECT;
  1229.       pe->obj=BM_GROUND;
  1230.     }
  1231.     mover= undoPos->mover;
  1232.     moves -= undoPos->mov;
  1233.     pushes -= undoPos->pus;
  1234.   }else{
  1235.     if(pb){
  1236.       bool isSel= pe==selected;
  1237.       mover=undoPos->mover;
  1238.       movePushR(pb,pe);
  1239.       if(isSel) setSelected(pb);
  1240.       if(mover!=undoPos->mover) sleep();
  1241.     }
  1242.     moveR(undoPos->mover);
  1243.   }
  1244.   undoing=false;
  1245.  }
  1246.  return true;
  1247. }
  1248. //---------------------------------------------------------------------------
  1249. //na konec
  1250. void undoAll()
  1251. {
  1252.  notdraw++;
  1253.  while(undo()) ;
  1254.  notdraw--;
  1255. }
  1256.  
  1257. void undoPus()
  1258. {
  1259.  if(editing){
  1260.    while(undo() && edUndo->obj==BM_MOVER) sleep();
  1261.  }else{
  1262.    do{
  1263.      undo();
  1264.      sleep();
  1265.    }while(undoPos>rec && !(undoPos-1)->pushBeg);
  1266.  }
  1267. }
  1268.  
  1269. void undo2()
  1270. {
  1271.  if(GetKeyState(VK_CONTROL)>=0){
  1272.    undoPus();
  1273.  }else{
  1274.    undo();
  1275.  }
  1276. }
  1277. //---------------------------------------------------------------------------
  1278. //vp°ed
  1279. bool redo()
  1280. {
  1281.  if(editing){
  1282.    if(!edRedo || edRedo==edUndo) return false;
  1283.    xchgEd();
  1284.    edUndo++;
  1285.  }else{
  1286.    if(!redoPos || redoPos==undoPos) return false;
  1287.    Psquare pb=undoPos->pushBeg, pe=undoPos->pushEnd;
  1288.    if(notdraw && !logPos){
  1289.      if(pb){
  1290.        pb->obj=BM_GROUND;
  1291.        pe->obj=BM_OBJECT;
  1292.      }
  1293.      mover= undoPos[1].mover;
  1294.      moves += undoPos->mov;
  1295.      pushes += undoPos->pus;
  1296.    }else{
  1297.      if(pb){
  1298.        bool isSel= pb==selected;
  1299.        movePush(pb, pe);
  1300.        if(isSel) setSelected(pe);
  1301.      }else{
  1302.        move(undoPos[1].mover);
  1303.      }
  1304.    }
  1305.    undoPos++;
  1306.  }
  1307.  return true;
  1308. }
  1309. //---------------------------------------------------------------------------
  1310. //na zaΦßtek
  1311. void redoAll()
  1312. {
  1313.  notdraw++;
  1314.  while(redo()) ;
  1315.  notdraw--;
  1316. }
  1317.  
  1318. void redoPus()
  1319. {
  1320.  if(editing){
  1321.    while(redo() && (edUndo-1)->obj==BM_MOVER) sleep();
  1322.  }else{
  1323.    int pus=pushes;
  1324.    while(redo() && pushes==pus) sleep();
  1325.  }
  1326. }
  1327.  
  1328. void redo2()
  1329. {
  1330.  if(GetKeyState(VK_CONTROL)>=0){
  1331.    redoPus();
  1332.  }else{
  1333.    redo();
  1334.  }
  1335. }
  1336. //---------------------------------------------------------------------------
  1337. //test na p°epln∞nφ undo bufferu
  1338. bool fullUndo()
  1339. {
  1340.  bool result= undoPos>=endA(rec)-1 || pushes>9900;
  1341.  if(result){
  1342.    msg(lng(800,"Too many moves"));
  1343.    movError++;
  1344.  }
  1345.  return result;
  1346. }
  1347. //---------------------------------------------------------------------------
  1348. //p°esun o jedno polφΦko ve sm∞ru direct
  1349. bool moveK(int direct)
  1350. {
  1351.  if(fullUndo()) return false;
  1352.  if(undoPos>rec && undoPos[-1].mover==nxtP(mover,direct) &&
  1353.    !undoPos[-1].pushBeg){
  1354.    undo();
  1355.    return true;
  1356.  }
  1357.  if(redoPos && redoPos>undoPos && undoPos[1].mover==nxtP(mover,direct)
  1358.    && (!undoPos->pushBeg || undoPos->pushBeg==undoPos[1].mover &&
  1359.     undoPos->pushEnd==nxtP(undoPos->pushBeg,direct))){
  1360.    redo();
  1361.    return true;
  1362.  }
  1363.  return move(direct,true);
  1364. }
  1365. //---------------------------------------------------------------------------
  1366. //p°esun hrßΦe na polφΦko p nejkratÜφ cestou
  1367. void moveM(Psquare p)
  1368. {
  1369.  if(fullUndo()) return; //p°etekl buffer na undo
  1370.  prepareUndo();
  1371.  int mov0=moves;
  1372.  if(move(p) > 1){
  1373.    undoPos->mov= short(moves-mov0);
  1374.    undoPos++;
  1375.  }
  1376. }
  1377. //---------------------------------------------------------------------------
  1378. //pohyb hrßΦe pomocφ Üipek na klßvesnici
  1379. void moveK2(int direct)
  1380. {
  1381.  Psquare p;
  1382.  int d1,d2;
  1383.  static int Y[]={0,1,3,2,6,7,4,5};
  1384.  static int X[]={1,0,2,3,5,4,7,6};
  1385.  
  1386.  if(fullUndo()) return;
  1387.  if(xtrans) direct=X[direct];
  1388.  if(ytrans) direct=Y[direct];
  1389.  if(direct>3){
  1390.    d1= direct & 1;
  1391.    d2= direct >> 1;
  1392.    if(nxtP(mover,d1)->obj==BM_GROUND || nxtP(mover,d2)->obj==BM_WALL){
  1393.      moveK(d1);
  1394.      moveK(d2);
  1395.    }else{
  1396.      moveK(d2);
  1397.      moveK(d1);
  1398.    }
  1399.  }else if(GetKeyState(VK_CONTROL)<0){
  1400.    //p°i stiÜt∞nΘm CTRL dojdi a₧ ke zdi nebo k bedn∞
  1401.    for(p=mover; p->obj==BM_GROUND; p=nxtP(p,direct)) ;
  1402.    moveM(prvP(p,direct));
  1403.  }else if(GetKeyState(VK_SHIFT)<0){
  1404.    //p°i stiÜt∞nΘm SHIFT dotlaΦ bednu a₧ ke zdi
  1405.    int Nobj=0;
  1406.    Psquare o=0;
  1407.    for(p=mover; p->obj<2; p=nxtP(p,direct)){
  1408.      if(p->obj==BM_OBJECT){
  1409.        Nobj++;
  1410.        if(Nobj>1) break;
  1411.        o=p;
  1412.      }
  1413.    }
  1414.    p=prvP(p,direct);
  1415.    if(o){
  1416.      if(o!=p) movePush(o,p,true); //p°esu≥ bednu
  1417.      else moveM(prvP(p,direct));  //bedna je a₧ u zdi, dojdi jen p°ed ni
  1418.    }else{
  1419.      //v cest∞ nenφ ₧ßdnß bedna
  1420.      moveM(p);
  1421.    }
  1422.  }else{
  1423.    //ud∞lej jeden krok nebo p°esun sousednφ bedny
  1424.    moveK(direct);
  1425.  }
  1426. }
  1427. //---------------------------------------------------------------------------
  1428. //test na p°epln∞nφ undo bufferu p°i editaci
  1429. void fullEdUndo()
  1430. {
  1431.  if(edUndo==endA(edRec)-1){
  1432.    //buffer na undo je u₧ pln²
  1433.    memmove(edRec,edRec+1,sizeof(edRec)-sizeof(EdUndo));
  1434.    edUndo--;
  1435.  }
  1436. }
  1437. //---------------------------------------------------------------------------
  1438. bool moveE(int direct)
  1439. {
  1440.  moverDirect=direct;
  1441.  Psquare old=mover;
  1442.  mover= nxtP(mover,direct);
  1443.  Psquare prev= prvP(old,direct);
  1444.  if(mover->obj!=BM_GROUND){
  1445.    mover=old;
  1446.    return false;
  1447.  }
  1448.  //zapiÜ undo
  1449.  fullEdUndo();
  1450.  edUndo->x= old->x;
  1451.  edUndo->y= old->y;
  1452.  edUndo->obj= BM_MOVER;
  1453.  if(prev->obj==BM_OBJECT && GetKeyState(VK_CONTROL)<0){
  1454.    //p°esun bedny
  1455.    old->obj= BM_OBJECT;
  1456.    prev->obj= BM_GROUND;
  1457.    paintSquare(prev);
  1458.    edUndo->obj++;
  1459.  }
  1460.  paintSquare(old);
  1461.  paintSquare(mover);
  1462.  edUndo++;
  1463.  edRedo=0;
  1464.  return true;
  1465. }
  1466. //---------------------------------------------------------------------------
  1467. //stisk tlaΦφtka na panelu p°ehrßvaΦe °eÜenφ
  1468. void replayBtn(int cmd)
  1469. {
  1470.  if(replay==cmd) return;
  1471.  if(!replay){
  1472.    //p°i spuÜt∞nφ p°ehrßvaΦe zobraz panel nßstroj∙
  1473.    ShowWindow(toolbar,SW_HIDE);
  1474.    ShowWindow(toolPlay,SW_SHOW);
  1475.    replay=cmd;
  1476.    resize();
  1477.  }
  1478.  //stiskni tlaΦφtko na panelu nßstroj∙
  1479.  SendMessage(toolPlay, TB_CHECKBUTTON, replay, MAKELONG(FALSE, 0));
  1480.  SendMessage(toolPlay, TB_CHECKBUTTON, cmd, MAKELONG(TRUE, 0));
  1481.  replay=cmd;
  1482. }
  1483. //---------------------------------------------------------------------------
  1484. //stisk tlaΦφtka myÜi p°i editaci
  1485. void editMouse(Psquare pos, WPARAM wP)
  1486. {
  1487.  fullEdUndo();
  1488.  edUndo->x= pos->x;
  1489.  edUndo->y= pos->y;
  1490.  edUndo->obj= pos->obj;
  1491.  edUndo->store= pos->store;
  1492.  if(wP & MK_CONTROL){
  1493.    //p°esun hrßΦe
  1494.    edUndo->x= mover->x;
  1495.    edUndo->y= mover->y;
  1496.    edUndo->obj= BM_MOVER;
  1497.    setMover(pos);
  1498.  }else if(wP & MK_SHIFT){
  1499.    //vytvo°enφ nebo smazßnφ ·lo₧iÜt∞
  1500.    pos->store= !pos->store;
  1501.  }else{
  1502.    if(ldown)
  1503.      //levΘ tlaΦφtko -> postavenφ nebo zbourßnφ zdi
  1504.      pos->obj= char(pos->obj==BM_WALL ? BM_GROUND : BM_WALL);
  1505.    else
  1506.      //pravΘ tlaΦφtko -> vytvo°enφ nebo smazßnφ bedny
  1507.      pos->obj= char(pos->obj==BM_OBJECT ? BM_GROUND : BM_OBJECT);
  1508.  }
  1509.  for(int i=0; i<9; i++){
  1510.    paintSquare(nxtP(pos,i));
  1511.  }
  1512.  edUndo++;
  1513.  edRedo=0;
  1514. }
  1515. //---------------------------------------------------------------------------
  1516. //pohyb myÜi p°i editaci
  1517. void editMouse(WPARAM wP,LPARAM lP)
  1518. {
  1519.  if(!ldown && !rdown) return; //nenφ stisknuto tlaΦφtko myÜi
  1520.  Psquare pos= SquareXY(LOWORD(lP), HIWORD(lP));
  1521.  if(!pos || pos->x<2 || pos->y<2 || pos->x>width-3 || pos->y>height-3 ||
  1522.     pos==editPos) return;
  1523.  //prove∩ zm∞ny na polφΦka mezi poslednφ a novou pozicφ myÜi
  1524.  int dir=direction(pos,editPos);
  1525.  for(Psquare p=pos; p!=editPos; p=nxtP(p,dir)){
  1526.    editMouse(p,wP);
  1527.    if(dir<0) break;
  1528.  }
  1529.  editPos= pos;
  1530. }
  1531. //---------------------------------------------------------------------------
  1532. int editDisabled[]= //polo₧ky v menu, kterΘ nejsou povoleny p°i editaci
  1533.  {112,115,204,206,217,401,402,403,405};
  1534.  
  1535. void setMenuName(int cmd, char *s)
  1536. {
  1537.  MENUITEMINFO mii;
  1538.  mii.cbSize=sizeof(MENUITEMINFO);
  1539.  mii.fMask=MIIM_TYPE;
  1540.  mii.fType=MFT_STRING;
  1541.  mii.dwTypeData= s;
  1542.  SetMenuItemInfo(GetMenu(hWin),cmd,FALSE,&mii);
  1543. }
  1544.  
  1545. void editMenu()
  1546. {
  1547.  setMenuName(114, editing ? lng(555,"Don&e\tEnter") :
  1548.    lng(556,"&Edit\tCtrl+E"));
  1549.  setMenuName(404, editing ? lng(557,"&Cancel edit\tEsc") :
  1550.    lng(558,"&Reset\tB"));
  1551.  for(int *u=editDisabled; u<endA(editDisabled); u++){
  1552.    EnableMenuItem(GetMenu(hWin),*u, MF_BYCOMMAND |
  1553.        (editing ? MF_GRAYED:MF_ENABLED));
  1554.  }
  1555. }
  1556.  
  1557. //ukonΦenφ editaΦnφho re₧imu
  1558. void cancelEdit()
  1559. {
  1560.  playtime=0;
  1561.  resetLevel();
  1562.  editing=false;
  1563.  setTitle(0);
  1564.  editMenu();
  1565. }
  1566.  
  1567. //p°echod do editaΦnφho re₧imu
  1568. void startEdit()
  1569. {
  1570.  fillOuter();
  1571.  resize();
  1572.  editing=true;
  1573.  setTitle(0);
  1574.  editMenu();
  1575. }
  1576.  
  1577. void stop()
  1578. {
  1579.  if(replay){
  1580.    KillTimer(hWin,200);
  1581.    replayBtn(205);
  1582.  }
  1583. }
  1584. //---------------------------------------------------------------------------
  1585. //zv∞tÜenφ Φasu o jednu sekundu
  1586. void clock()
  1587. {
  1588.  if(IsIconic(hWin)) return; //okno je minimalizovanΘ
  1589.  if(editing || replay){
  1590.    //p°i editaci nebo p°ehrßvaΦi se hodiny nezobrazujφ
  1591.    status(4,"");
  1592.  }else{
  1593.    playtime++;
  1594.    if(!stopTime){
  1595.      //zobraz Φas na stavovΘm °ßdku
  1596.      int t=playtime;
  1597.      status(4,"%02d:%02d:%02d",t/3600,(t/60)%60,t%60);
  1598.    }
  1599.  }
  1600. }
  1601. //---------------------------------------------------------------------------
  1602. void langChanged()
  1603. {
  1604.  static int subId[]={606,605,604,603,602,600,601};
  1605.  loadMenu(hWin,"MENU",subId);
  1606.  editMenu();
  1607.  DrawMenuBar(hWin);
  1608.  //p°ekresli stavov² °ßdek
  1609.  status();
  1610.  //zm∞≥ texty v open/save dialogu
  1611.  savOfn.lpstrFilter=lng(801,"Saved positions (*.sav)\0*.sav\0All files\0*.*\0");
  1612.  userOfn.lpstrFilter=lng(802,"Player's solutions (*.rec)\0*.rec\0All files\0*.*\0");
  1613.  skinOfn.lpstrFilter=lng(803,"Skins (*.bmp)\0*.bmp\0");
  1614.  dataOfn.lpstrFilter=lng(804,"Levels database\0*.*\0");
  1615.  levelOfn.lpstrFilter=lng(805,"Sokoban levels (*.xsb)\0*.xsb\0Text files (*.txt)\0*.txt\0All files\0*.*\0");
  1616. }
  1617. //---------------------------------------------------------------------------
  1618. //dialog mo₧nosti
  1619. BOOL CALLBACK OptionsProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM )
  1620. {
  1621.  static struct{ int *prom,id; } D[]={
  1622.   {&moveDelay,101},
  1623.   {&playTimer,102},
  1624.   {&fastTimer,103},
  1625.   {&toolBarVisible,511},
  1626.   {&statusBarVisible,512},
  1627.   {¢er,513},
  1628.   {&nowalls,514},
  1629.   {&noobjects,515},
  1630.  };
  1631.  int i;
  1632.  
  1633.  switch(mesg){
  1634.   case WM_INITDIALOG:
  1635.    setDlgTexts(hWnd,510);
  1636.    for(i=0; i<sizeof(D)/sizeof(*D); i++){
  1637.      if(D[i].id>=300){
  1638.        CheckDlgButton(hWnd, D[i].id, *D[i].prom ? BST_CHECKED:BST_UNCHECKED);
  1639.      }else{
  1640.        SetDlgItemInt(hWnd, D[i].id, *D[i].prom, FALSE);
  1641.      }
  1642.    }
  1643.   return TRUE;
  1644.  
  1645.   case WM_COMMAND:
  1646.    wP=LOWORD(wP);
  1647.    switch(wP){
  1648.     case IDOK:{
  1649.      for(i=0; i<sizeof(D)/sizeof(*D); i++){
  1650.        if(D[i].id>=300){
  1651.          *D[i].prom= IsDlgButtonChecked(hWnd,D[i].id);
  1652.        }else{
  1653.          *D[i].prom= GetDlgItemInt(hWnd,D[i].id, NULL, FALSE);
  1654.        }
  1655.      }
  1656.      ShowWindow(toolbar,toolBarVisible && !replay ? SW_SHOW:SW_HIDE);
  1657.      ShowWindow(hStatus,statusBarVisible ? SW_SHOW:SW_HIDE);
  1658.      resize();
  1659.     }
  1660.     case IDCANCEL:
  1661.      EndDialog(hWnd, wP);
  1662.     return TRUE;
  1663.    }
  1664.   break;
  1665.  }
  1666.  return FALSE;
  1667. }
  1668. //---------------------------------------------------------------------------
  1669. //dialog p°ejφt na zvolenou mφstnost
  1670. BOOL CALLBACK GotoProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM )
  1671. {
  1672.  switch(mesg){
  1673.   case WM_INITDIALOG:
  1674.    setDlgTexts(hWnd,519);
  1675.    SetDlgItemInt(hWnd,101, prevLevel+1, FALSE);
  1676.   return TRUE;
  1677.   case WM_COMMAND:
  1678.    wP=LOWORD(wP);
  1679.    switch(wP){
  1680.     case IDOK:
  1681.      prevLevel=level;
  1682.      loadLevel(GetDlgItemInt(hWnd,101,NULL,FALSE)-1);
  1683.     case IDCANCEL:
  1684.      EndDialog(hWnd, wP);
  1685.     return TRUE;
  1686.    }
  1687.   break;
  1688.  }
  1689.  return FALSE;
  1690. }
  1691. //---------------------------------------------------------------------------
  1692. //dialog jφt na zadan² tah
  1693. BOOL CALLBACK MovProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM )
  1694. {
  1695.  switch(mesg){
  1696.   case WM_INITDIALOG:
  1697.    setDlgTexts(hWnd,521);
  1698.    SetDlgItemInt(hWnd,101, moves, FALSE);
  1699.   return TRUE;
  1700.   case WM_COMMAND:
  1701.    wP=LOWORD(wP);
  1702.    switch(wP){
  1703.     case IDOK:{
  1704.      int mov= GetDlgItemInt(hWnd,101,NULL,FALSE);
  1705.      notdraw++;
  1706.      undoAll();
  1707.      while(moves<mov && redo()) ;
  1708.      notdraw--;
  1709.      update();
  1710.     }
  1711.     case IDCANCEL:
  1712.      EndDialog(hWnd, wP);
  1713.     return TRUE;
  1714.    }
  1715.   break;
  1716.  }
  1717.  return FALSE;
  1718. }
  1719. //---------------------------------------------------------------------------
  1720. BOOL CALLBACK AboutProc(HWND hWnd, UINT msg, WPARAM wP, LPARAM )
  1721. {
  1722.  switch(msg){
  1723.   case WM_INITDIALOG:
  1724.    setDlgTexts(hWnd,11);
  1725.    return TRUE;
  1726.  
  1727.   case WM_COMMAND:
  1728.    wP=LOWORD(wP);
  1729.    switch(wP){
  1730.     case IDOK:
  1731.     case IDCANCEL:
  1732.      EndDialog(hWnd, wP);
  1733.     return TRUE;
  1734.    }
  1735.   break;
  1736.  }
  1737.  return 0;
  1738. }
  1739. //---------------------------------------------------------------------------
  1740. //dotaz na jmΘno autora novΘ mφstnosti
  1741. BOOL CALLBACK AuthorProc(HWND hWnd, UINT msg, WPARAM wP, LPARAM )
  1742. {
  1743.  switch(msg){
  1744.   case WM_INITDIALOG:
  1745.    setDlgTexts(hWnd,523);
  1746.    SetDlgItemText(hWnd,101,curAuthor);
  1747.    CheckRadioButton(hWnd,525,526, 525+clone);
  1748.    return TRUE;
  1749.  
  1750.   case WM_COMMAND:
  1751.    wP=LOWORD(wP);
  1752.    switch(wP){
  1753.     case IDOK:
  1754.      GetDlgItemText(hWnd,101,curAuthor,sizeof(curAuthor));
  1755.      clone= getRadioButton(hWnd,525,526);
  1756.     case IDCANCEL:
  1757.      EndDialog(hWnd, wP);
  1758.     return TRUE;
  1759.    }
  1760.   break;
  1761.  }
  1762.  return 0;
  1763. }
  1764. //---------------------------------------------------------------------------
  1765. int countSolved()
  1766. {
  1767.  int solved=0;
  1768.  for(int i=0; i<Nlevels; i++){
  1769.    Level *lev= &levoff[i];
  1770.    if(lev->user.Mmoves) solved++;
  1771.  }
  1772.  return solved;
  1773. }
  1774. //---------------------------------------------------------------------------
  1775. int sortLevel(const void *a, const void *b)
  1776. {
  1777.  return descending*int((*(Level**)b)-(*(Level**)a));
  1778. }
  1779. int sortBest(const void *a, const void *b)
  1780. {
  1781.  return descending*((*(Level**)a)->best.Mpushes+(*(Level**)a)->best.Mmoves
  1782.   -(*(Level**)b)->best.Mpushes-(*(Level**)b)->best.Mmoves);
  1783. }
  1784. int sortUser(const void *a, const void *b)
  1785. {
  1786.  return descending*((*(Level**)a)->user.Mpushes+(*(Level**)a)->user.Mmoves
  1787.   -(*(Level**)b)->user.Mpushes-(*(Level**)b)->user.Mmoves);
  1788. }
  1789. int sortEq(const void *a, const void *b)
  1790. {
  1791.  return descending*((*(Level**)b)->user.eval()-(*(Level**)b)->best.eval()
  1792.   -(*(Level**)a)->user.eval()+(*(Level**)a)->best.eval());
  1793. }
  1794. int sortDim(const void *a, const void *b)
  1795. {
  1796.  return descending*((*(Level**)b)->i-(*(Level**)a)->i);
  1797. }
  1798. int sortAuthor(const void *a, const void *b)
  1799. {
  1800.  return descending*_stricmp((*(Level**)b)->author,(*(Level**)a)->author);
  1801. }
  1802. int sortObj(const void *a, const void *b)
  1803. {
  1804.  return descending*((*(Level**)b)->i-(*(Level**)a)->i);
  1805. }
  1806.  
  1807. void sortList()
  1808. {
  1809.  Level *lev;
  1810.  int i;
  1811.  static int (*F[])(const void*,const void*)={sortLevel,sortBest,sortEq,sortUser,sortObj,sortDim,sortAuthor};
  1812.  
  1813.  for(i=0; i<Nlevels; i++){
  1814.    lev= &levoff[i];
  1815.    A[i]=lev;
  1816.    getDim(lev->offset,lev->width,lev->height);
  1817.    if(F[sortedCol]==sortDim){
  1818.      lev->i = lev->width*lev->height;
  1819.    }
  1820.    if(F[sortedCol]==sortObj){
  1821.      lev->i = getNobj(lev->offset);
  1822.    }
  1823.  }
  1824.  qsort(A,Nlevels,sizeof(Level*),F[sortedCol]);
  1825. }
  1826. //---------------------------------------------------------------------------
  1827. //zobrazenφ seznamu vÜech mφstnostφ
  1828. BOOL CALLBACK LevelsProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM lP)
  1829. {
  1830.  static char *colNames[]=
  1831.   {"Level","Best solution","","Your solution","Objects","Dimension","Author"};
  1832.  
  1833.  static Level *first,*last;
  1834.  int item,notif,i,y;
  1835.  HWND listBox = GetDlgItem(hWnd,101);
  1836.  HWND header = GetDlgItem(hWnd,102);
  1837.  RECT rc;
  1838.  HD_LAYOUT hdl;
  1839.  WINDOWPOS wp;
  1840.  TEXTMETRIC tm;
  1841.  HD_ITEM hdi;
  1842.  DRAWITEMSTRUCT *lpdis;
  1843.  MEASUREITEMSTRUCT *lpmis;
  1844.  Level *lev;
  1845.  char buf[128];
  1846.  
  1847.  switch(mesg){
  1848.   case WM_INITDIALOG:
  1849.    setDlgTexts(hWnd,527);
  1850.    A=new Level*[Nlevels];
  1851.    sortList();
  1852.    //vytvo° header control
  1853.    header= CreateWindowEx(0, WC_HEADER, 0,
  1854.      WS_CHILD | WS_BORDER | HDS_BUTTONS | HDS_HORZ,
  1855.      0,0,0,0, hWnd, (HMENU) 102, inst, 0);
  1856.    SendMessage(header, WM_SETFONT, SendMessage(hWnd,WM_GETFONT,0,0),0);
  1857.    //vypl≥ nßzvy sloupc∙
  1858.    for(i=0; i<sizeA(colNames); i++){
  1859.      hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
  1860.      hdi.pszText = lng(530+colOrder[i],colNames[colOrder[i]]);
  1861.      hdi.cchTextMax = (int)strlen(hdi.pszText);
  1862.      hdi.cxy = colWidth[i];
  1863.      hdi.fmt = HDF_LEFT | HDF_STRING;
  1864.      Header_InsertItem(header,i,&hdi);
  1865.    }
  1866.    //spoΦti poΦet vy°eÜen²ch mφstnostφ
  1867.    SetDlgItemInt(hWnd,104,countSolved(),FALSE);
  1868.    //nastav velikost okna
  1869.    MoveWindow(hWnd,levDlgX,levDlgY,10,10,FALSE); //aby se zavolalo W_SIZE
  1870.    MoveWindow(hWnd,levDlgX,levDlgY,levDlgW,levDlgH,FALSE);
  1871.    //nastav kurzor doprost°ed okna
  1872.    SendMessage(listBox, LB_SETCOUNT, Nlevels, 0);
  1873.    for(i=0; i<Nlevels; i++){
  1874.      if(A[i]==&levoff[level]){
  1875.        SendMessage(listBox, LB_SETCURSEL, i+6, 0);
  1876.        SendMessage(listBox, LB_SETCURSEL, i, 0);
  1877.        break;
  1878.      }
  1879.    }
  1880.    first=last=0;
  1881.   return TRUE;
  1882.  
  1883.   case WM_GETMINMAXINFO:
  1884.    ((MINMAXINFO FAR*) lP)->ptMinTrackSize.x = 430;
  1885.    ((MINMAXINFO FAR*) lP)->ptMinTrackSize.y = 160;
  1886.   break;
  1887.  
  1888.   case WM_MEASUREITEM:
  1889.    lpmis = (LPMEASUREITEMSTRUCT) lP;
  1890.    lpmis->itemHeight = HIWORD(GetDialogBaseUnits())+1;
  1891.    return TRUE;
  1892.  
  1893.   case WM_DRAWITEM:
  1894.    lpdis = (LPDRAWITEMSTRUCT) lP;
  1895.    if(lpdis->itemID == -1) break;
  1896.    switch(lpdis->itemAction){
  1897.     case ODA_DRAWENTIRE:
  1898.      lev= A[lpdis->itemID];
  1899.      GetTextMetrics(lpdis->hDC, &tm);
  1900.      rc.top= lpdis->rcItem.top;
  1901.      rc.bottom= lpdis->rcItem.bottom;
  1902.      rc.right=4;
  1903.      for(i=0; i<sizeA(colWidth); i++){
  1904.        switch(colOrder[i]){
  1905.         case 0:
  1906.          sprintf(buf, "%d", lev-levoff+1);
  1907.         break;
  1908.         case 1:
  1909.          sprintf(buf, "%d - %d", lev->best.Mmoves,lev->best.Mpushes);
  1910.         break;
  1911.         case 2:
  1912.          buf[0]=0;
  1913.          if(lev->best.Mmoves && lev->user.Mmoves){
  1914.            if(lev->best.eval()==lev->user.eval()) buf[0]= '=';
  1915.          }
  1916.          buf[1]=0;
  1917.         break;
  1918.         case 3:
  1919.          sprintf(buf, "%d - %d", lev->user.Mmoves,lev->user.Mpushes);
  1920.         break;
  1921.         case 4:
  1922.          sprintf(buf, "%d", getNobj(lev->offset));
  1923.         break;
  1924.         case 5:
  1925.          sprintf(buf, "%d x %d", lev->width, lev->height);
  1926.         break;
  1927.         case 6:
  1928.          strcpy(buf,lev->author);
  1929.         break;
  1930.        }
  1931.        rc.left=rc.right;
  1932.        rc.right+=colWidth[i];
  1933.        DrawText(lpdis->hDC, buf, (int)strlen(buf), &rc, DT_END_ELLIPSIS|DT_NOPREFIX);
  1934.      }
  1935.     case ODA_SELECT:
  1936.      SelectObject(lpdis->hDC, GetStockObject(NULL_BRUSH));
  1937.      SelectObject(lpdis->hDC, GetStockObject(WHITE_PEN));
  1938.      Rectangle(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top,
  1939.        lpdis->rcItem.right, lpdis->rcItem.bottom);
  1940.      if(lpdis->itemState & ODS_SELECTED){
  1941.        DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
  1942.      }
  1943.    }
  1944.    return TRUE;
  1945.  
  1946.   case WM_SIZE:
  1947.    //p°i zm∞n∞ velikosti okna zm∞≥ polohy vÜech ovlßdacφch prvk∙
  1948.    GetClientRect(hWnd,&rc);
  1949.    rc.bottom -= 3*HIWORD(GetDialogBaseUnits())+10;
  1950.    hdl.prc = &rc;
  1951.    hdl.pwpos = ℘
  1952.    Header_Layout(header, &hdl);
  1953.    SetWindowPos(listBox,0,0,wp.cy,rc.right,rc.bottom, SWP_NOZORDER);
  1954.    SetWindowPos(header, wp.hwndInsertAfter, 0,0,
  1955.      wp.cx,wp.cy+1, wp.flags|SWP_SHOWWINDOW);
  1956.    y= HIWORD(lP)-2*HIWORD(GetDialogBaseUnits());
  1957.    setWindowXY(GetDlgItem(hWnd,IDOK), int(LOWORD(lP)*0.33), y);
  1958.    setWindowXY(GetDlgItem(hWnd,IDCANCEL), int(LOWORD(lP)*0.61), y);
  1959.    setWindowXY(GetDlgItem(hWnd,528), 4, y+2);
  1960.    setWindowXY(GetDlgItem(hWnd,104),9*LOWORD(GetDialogBaseUnits()), y+2);
  1961.   break;
  1962.  
  1963.   case WM_NOTIFY:
  1964.   {
  1965.    HD_NOTIFY *nmhdr = (HD_NOTIFY*) lP;
  1966.    switch(nmhdr->hdr.code){
  1967.     case HDN_TRACK:
  1968.      colWidth[nmhdr->iItem]= nmhdr->pitem->cxy;
  1969.      Header_SetItem(header,nmhdr->iItem,nmhdr->pitem);
  1970.      InvalidateRect(listBox,0,TRUE);
  1971.     break;
  1972.     case HDN_ITEMCLICK:
  1973.      if(sortedCol==nmhdr->iItem){
  1974.        descending= -descending;
  1975.      }else{
  1976.        sortedCol=nmhdr->iItem;
  1977.        descending= -1;
  1978.      }
  1979.      sortList();
  1980.      InvalidateRect(listBox,0,TRUE);
  1981.     break;
  1982.    }
  1983.    break;
  1984.   }
  1985.   case WM_VKEYTOITEM:
  1986.    item= (int) SendMessage(listBox, LB_GETCURSEL,0,0);
  1987.    lev=0;
  1988.    if(item>=0 && item<Nlevels) lev=A[item];
  1989.    switch(LOWORD(wP)){
  1990.     case 'P':
  1991.      item-=2;
  1992.     case 'N':
  1993.      item++;
  1994.     case ' ':
  1995.      if(item>=0 && item<Nlevels){
  1996.        loadLevel(int(A[item]-levoff));
  1997.        SetFocus(listBox);
  1998.      }
  1999.     break;
  2000.     case 'B':
  2001.      first=lev;
  2002.     break;
  2003.     case 'E':
  2004.      last=lev;
  2005.     break;
  2006.     case 'V':
  2007.      if(sortedCol==0){
  2008.        movLevels(first,last,lev);
  2009.        InvalidateRect(listBox,0,TRUE);
  2010.      }
  2011.     break;
  2012.     case VK_DELETE:
  2013.      if(delLevel(int(lev-levoff),hWnd)){
  2014.        if(item==Nlevels) item--;
  2015.        SetDlgItemInt(hWnd,104,countSolved(),FALSE);
  2016.        SendMessage(listBox, LB_SETCOUNT, Nlevels, 0);
  2017.        sortList();
  2018.        InvalidateRect(listBox,0,TRUE);
  2019.      }
  2020.     break;
  2021.     default:
  2022.      return -1;               
  2023.    }
  2024.    PostMessage(listBox, LB_SETCURSEL, item+6, 0);
  2025.    PostMessage(listBox, LB_SETCURSEL, item, 0);
  2026.    return -2;
  2027.  
  2028.   case WM_COMMAND:
  2029.    notif = HIWORD(wP);
  2030.    wP=LOWORD(wP);
  2031.    switch(wP){
  2032.     case 101: //listBox
  2033.      if(notif!=LBN_DBLCLK) return FALSE;
  2034.     case IDOK:
  2035.      prevLevel=level;
  2036.      item= (int) SendMessage(listBox, LB_GETCURSEL,0,0);
  2037.      if(item>=0 && item<Nlevels){
  2038.        i=int(A[item]-levoff);
  2039.        if(i!=level) loadLevel(i);
  2040.      }
  2041.     case IDCANCEL:
  2042.      GetWindowRect(hWnd,&rc);
  2043.      levDlgX=rc.left;
  2044.      levDlgY=rc.top;
  2045.      levDlgW=rc.right-rc.left;
  2046.      levDlgH=rc.bottom-rc.top;
  2047.      delete[] A;
  2048.      EndDialog(hWnd,wP);
  2049.     return TRUE;
  2050.    }
  2051.   break;
  2052.  }
  2053.  return FALSE;
  2054. }
  2055. //---------------------------------------------------------------------------
  2056. //v²b∞r mezi °eÜenφm u₧ivatele a nejlepÜφm °eÜenφm v databßzi
  2057. BOOL CALLBACK SolutionProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM )
  2058. {
  2059.  switch(mesg){
  2060.   case WM_INITDIALOG:{
  2061.    setDlgTexts(hWnd,540);
  2062.    CheckDlgButton(hWnd,541,BST_CHECKED);
  2063.    Level *lev= &levoff[level];
  2064.    Pchar dat=0;
  2065.    int n=0;
  2066.    Pchar s[2]= { lev->best.Mdata, lev->user.Mdata };
  2067.    for(int i=0; i<2; i++){
  2068.      if(s[i]){
  2069.        dat=s[i];
  2070.        n++;
  2071.      }
  2072.    }
  2073.    if(s[0] && s[1] && !strcmp(s[0],s[1])) n--;
  2074.    if(!n) msg(lng(807,"This level has not been solved"));
  2075.    if(n<2) EndDialog(hWnd,(int)dat);
  2076.    return TRUE;
  2077.   }
  2078.  
  2079.   case WM_COMMAND:
  2080.    wP=LOWORD(wP);
  2081.    switch(wP){
  2082.     case IDOK:{
  2083.      Pchar dat=0;
  2084.      Level *lev= &levoff[level];
  2085.      if(IsDlgButtonChecked(hWnd,541)) dat=lev->best.Mdata;
  2086.      if(IsDlgButtonChecked(hWnd,542)) dat=lev->user.Mdata;
  2087.      EndDialog(hWnd,(int)dat);
  2088.     }
  2089.     return TRUE;
  2090.     case IDCANCEL:
  2091.      EndDialog(hWnd, 0);
  2092.     return TRUE;
  2093.    }
  2094.   break;
  2095.  }
  2096.  return FALSE;
  2097. }
  2098. //---------------------------------------------------------------------------
  2099. LRESULT CALLBACK MainWndProc(HWND hWnd, UINT mesg, WPARAM wP, LPARAM lP)
  2100. {
  2101.  switch (mesg) {
  2102.  case WM_COMMAND:
  2103.   wP=LOWORD(wP);
  2104.   if(wP<211 || wP>216) setSelected(0);
  2105.   if(setLang(wP)) break;
  2106.   if(editing){
  2107.    switch(wP){
  2108.     case 114:{ //editor
  2109.      check++;
  2110.      int e=optimizeLevel();
  2111.      check--;
  2112.      if(!e){
  2113.       if(wrLevel()) cancelEdit();
  2114.       else fillOuter();
  2115.       repaint();
  2116.      }
  2117.     }
  2118.     break;
  2119.     case 101: //escape
  2120.     case 404: //reset level
  2121.      if(level==Nlevels-1 && !levoff[level].offset){
  2122.        Nlevels--;
  2123.        level--;
  2124.      }
  2125.      cancelEdit();
  2126.     break;
  2127.     case 106: //open position
  2128.     case 407: //open level
  2129.      notOptimize++;
  2130.      openLevel();
  2131.      notOptimize--;
  2132.     break;
  2133.     case 107: //save position
  2134.      saveLevel();
  2135.     break;
  2136.     case 110:{ //quick save
  2137.      delete[] edQsave;
  2138.      edQsave= new Square1[width*height];
  2139.      Psquare1 dest;
  2140.      Psquare src;
  2141.      for(src=board,dest=edQsave; src<boardk; src++,dest++){
  2142.        dest->obj= src->obj;
  2143.        dest->store= src->store;
  2144.      }
  2145.      edQsaveMoverX= mover->x;
  2146.      edQsaveMoverY= mover->y;
  2147.      edQsaveWidth= width;
  2148.      edQsaveHeight= height;
  2149.     }
  2150.     break;
  2151.     case 111: //quick load
  2152.      if(!edQsave || width!=edQsaveWidth || height!=edQsaveHeight){
  2153.       msg(lng(808,"Nothing to load"));
  2154.      }else{
  2155.       Psquare dest;
  2156.       Psquare1 src;
  2157.       for(dest=board,src=edQsave; dest<boardk; src++,dest++){
  2158.         dest->obj= src->obj;
  2159.         dest->store= src->store;
  2160.       }
  2161.       mover=square(edQsaveMoverX,edQsaveMoverY);
  2162.       edUndo=edRec;
  2163.       edRedo=0;
  2164.       repaint();
  2165.      }
  2166.     break;
  2167.     case 408:{ //delete level
  2168.      clearBoard();
  2169.      repaint();
  2170.     }
  2171.     break;
  2172.     case 450:
  2173.     case 451:
  2174.      if(!mover->store){
  2175.        for(Psquare p=board; p<boardk; p++)
  2176.          if(p->obj==BM_GROUND || p->obj==BM_OBJECT)
  2177.            p->obj= (char) (p->store ? BM_OBJECT:BM_GROUND);
  2178.      }
  2179.     case 452:
  2180.      gener();
  2181.     break;
  2182.    }
  2183.   }else{ //not editing
  2184.    switch(wP){
  2185.     case 101: //escape
  2186.      if(replay) SendMessage(hWnd,WM_COMMAND,210,0);
  2187.      else ShowWindow(hWnd,SW_MINIMIZE);
  2188.     break;
  2189.     case 106: //open position
  2190.      openPos();
  2191.     break;
  2192.     case 107: //save position
  2193.      savePos();
  2194.     break;
  2195.     case 110: //quick save
  2196.      if(undoPos==rec){
  2197.       msg(lng(809,"Nothing to save"));
  2198.      }else{
  2199.       delete[] quickSave;
  2200.       quickSaveLevel=level;
  2201.       undoPos->mover=mover;
  2202.       quickSave = new QuickInfo[quickLen = int(undoPos-rec)+1];
  2203.       for(int i=0; i<quickLen; i++){
  2204.         QuickInfo *q= &quickSave[i];
  2205.         q->mover= subBoard(rec[i].mover);
  2206.         q->pushBeg= subBoard(rec[i].pushBeg);
  2207.         q->pushEnd= subBoard(rec[i].pushEnd);
  2208.         q->mov= rec[i].mov;
  2209.         q->pus= rec[i].pus;
  2210.       }
  2211.      }
  2212.     break;
  2213.     case 111: //quick load
  2214.      if(!quickSave){
  2215.       msg(lng(808,"Nothing to load"));
  2216.      }else{
  2217.       if(level!=quickSaveLevel){
  2218.        if( msg1(MB_YESNO|MB_ICONQUESTION,
  2219.         lng(810,"Position has been saved in level %d.\r\nDo you want to restore it ?"),
  2220.         quickSaveLevel+1) == IDNO) break;
  2221.        loadLevel(quickSaveLevel);
  2222.       }
  2223.       undoAll();
  2224.       for(int i=0; i<quickLen; i++){
  2225.         QuickInfo *q= &quickSave[i];
  2226.         rec[i].mover= addBoard(q->mover);
  2227.         rec[i].pushBeg= addBoard(q->pushBeg);
  2228.         rec[i].pushEnd= addBoard(q->pushEnd);
  2229.         rec[i].mov= q->mov;
  2230.         rec[i].pus= q->pus;
  2231.       }
  2232.       redoPos= rec+quickLen-1;
  2233.       undoPos= rec;
  2234.       redoAll();
  2235.       update();
  2236.      }
  2237.     break;
  2238.     case 114: //editor
  2239.      if(replay) SendMessage(hWnd,WM_COMMAND,210,0);
  2240.      notMsg++;
  2241.      undoAll();
  2242.      notMsg--;
  2243.      newBoard(0,0,1);
  2244.      startEdit();
  2245.     break;
  2246.     case 115: //new level
  2247.      if(replay) SendMessage(hWnd,WM_COMMAND,210,0);
  2248.      if(DialogBox(inst,"AUTHOR",hWnd,(DLGPROC)AuthorProc)==IDOK){
  2249.       if(clone) prevLevel=level;
  2250.       Level *lev= addLevel();
  2251.       if(lev){
  2252.        int len=(int)strlen(curAuthor);
  2253.        if(len){
  2254.          lev->author= new char[len+1];
  2255.          strcpy(lev->author,curAuthor);
  2256.        }
  2257.        newBoard(0,0,clone);
  2258.        status();
  2259.        startEdit();
  2260.       }
  2261.      }
  2262.     break;
  2263.     case 202: //rewind
  2264.     case 203: //back
  2265.     case 207: //play
  2266.     case 208: //fast forward
  2267.      if(replay){
  2268.        int cmd= (int)wP;
  2269.        if(replay==cmd){
  2270.          if(cmd==207 || cmd==202) cmd++;
  2271.          else if(cmd==208 || cmd==203) cmd--;
  2272.        }
  2273.        replayBtn(cmd);
  2274.        aminmax(playTimer,10,9000);
  2275.        aminmax(fastTimer,0,playTimer);
  2276.        SetTimer(hWin,200, cmd==202 || cmd==208 ? fastTimer : playTimer,0);
  2277.      }
  2278.     break;
  2279.     case 204: //undo
  2280.      stop();
  2281.      undo();
  2282.     break;
  2283.     case 205: //pause
  2284.      stop();
  2285.     break;
  2286.     case 206: //redo
  2287.      stop();
  2288.      redo();
  2289.     break;
  2290.     case 112: //play solution
  2291.     case 210: //stop
  2292.      if(replay){
  2293.        stop();
  2294.        replay=0;
  2295.        ShowWindow(toolPlay,SW_HIDE);
  2296.        if(toolBarVisible) ShowWindow(toolbar,SW_SHOW);
  2297.        resize();
  2298.        SetFocus(hWin);
  2299.      }else{
  2300.        Pchar p= (Pchar)DialogBox(inst,"SOLUTION",
  2301.          hWnd,(DLGPROC)SolutionProc);
  2302.        if(p){
  2303.          loadSolution(level,p);
  2304.          if(movError){
  2305.            msg(lng(811,"Solution is wrong !"));
  2306.          }
  2307.          undoAll();
  2308.          update();
  2309.          replayBtn(205);
  2310.        }
  2311.      }
  2312.     break;
  2313.     case 217: //goto move
  2314.      DialogBox(inst,"MOV",hWnd,(DLGPROC)MovProc);
  2315.     break;
  2316.     case 401: //next level
  2317.      loadLevel(level+1);
  2318.      UpdateWindow(hWin);
  2319.     break;
  2320.     case 402: //previous level
  2321.      loadLevel(level-1);
  2322.      UpdateWindow(hWin);
  2323.     break;
  2324.     case 403: //list levels
  2325.      DialogBox(inst,"LEVEL",hWnd,(DLGPROC)LevelsProc);
  2326.     break;
  2327.     case 405: //go to level
  2328.      DialogBox(inst,"GOTO",hWnd,(DLGPROC)GotoProc);
  2329.     break;
  2330.     case 407: //open level
  2331.      check++;
  2332.      openLevel();
  2333.      check--;
  2334.     break;
  2335.     case 408: //delete level
  2336.      delLevel(level,hWin);
  2337.     break;
  2338.     case 450:
  2339.     case 451:
  2340.     case 452:
  2341.      undoAll();
  2342.      update();
  2343.      findSolution((int)wP-450);
  2344.     break;
  2345.    }
  2346.   }
  2347.   switch(wP){
  2348.     case 100: //about
  2349.      DialogBox(inst, "ABOUT", hWnd, (DLGPROC) AboutProc);
  2350.     break;
  2351.     case 350: //help
  2352.      getExeDir(fnhelp,lng(13,"sokob_en.hlp"));
  2353.      WinHelp(hWin,fnhelp,HELP_FINDER,0);
  2354.     break;
  2355.     case 103: //exit
  2356.      SendMessage(hWin,WM_CLOSE,0,0);
  2357.     break;
  2358.     case 104: //options
  2359.      DialogBox(inst,"OPTIONS",hWnd,(DLGPROC)OptionsProc);
  2360.     break;
  2361.     case 105: //delete settings
  2362.      if(MessageBox(hWnd, lng(736,"Do you want to delete your settings ?"),
  2363.       title, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) ==IDYES){
  2364.        deleteini();
  2365.      }
  2366.     break;
  2367.     case 108: //save all
  2368.      modifUser=true;
  2369.      saveUser();
  2370.      saveData();
  2371.     break;
  2372.     case 109: //change player
  2373.      openUser();
  2374.     break;
  2375.     case 116: //mirror X
  2376.      xtrans=!xtrans;
  2377.      repaint();
  2378.      CheckMenuItem(GetMenu(hWin),(UINT)wP,
  2379.        MF_BYCOMMAND|(xtrans ? MF_CHECKED:MF_UNCHECKED));
  2380.     break;
  2381.     case 117: //mirror Y
  2382.      ytrans=!ytrans;
  2383.      repaint();
  2384.      CheckMenuItem(GetMenu(hWin),(UINT)wP,
  2385.        MF_BYCOMMAND|(ytrans ? MF_CHECKED:MF_UNCHECKED));
  2386.     break;
  2387.     case 201: //begin
  2388.      undoAll();
  2389.      update();
  2390.     break;
  2391.     case 209: //end
  2392.      redoAll();
  2393.      update();
  2394.     break;
  2395.     case 211: //undo push
  2396.      undoPus();
  2397.     break;
  2398.     case 212: //redo push
  2399.      redoPus();
  2400.     break;
  2401.     case 213: //undo move
  2402.      undo();
  2403.     break;
  2404.     case 214: //redo move
  2405.      redo();
  2406.     break;
  2407.     case 215: //undo
  2408.      undo2();
  2409.     break;
  2410.     case 216: //redo
  2411.      redo2();
  2412.     break;
  2413.     case 301: //open skin
  2414.      openSkin();
  2415.     break;
  2416.     case 302: //next skin
  2417.      nextSkin();
  2418.     break;
  2419.     case 303: //previous skin
  2420.      prevSkin();
  2421.     break;
  2422.     case 304: //reload skin
  2423.      loadSkin();
  2424.     break;
  2425.     case 404: //reset level
  2426.      if(replay) SendMessage(hWnd,WM_COMMAND,201,0);
  2427.      else resetLevel();
  2428.     break;
  2429.     case 406: //save level
  2430.      saveLevel();
  2431.     break;
  2432.   }
  2433.  break;
  2434.  
  2435.  case WM_TIMER:
  2436.   switch(wP){
  2437.    case 128:
  2438.     clock();
  2439.    break;
  2440.    case 200:
  2441.     if(replay>205) redo();
  2442.     else undo();
  2443.    break;
  2444.    case 300:
  2445.     KillTimer(hWin,wP);
  2446.     setTitle(0);
  2447.    break;
  2448.   }
  2449.  break;
  2450.  
  2451.  case WM_MOUSEMOVE:{
  2452.   Psquare mouse= SquareXY(LOWORD(lP), HIWORD(lP));
  2453.   if(mouse){
  2454.    static Psquare last;
  2455.    if(mouse!=last){
  2456.     status(5, "%d:%d", mouse->x,mouse->y);
  2457.     last=mouse;
  2458.    }                             
  2459.   }else{
  2460.    status(5,"");
  2461.   }
  2462.   if(editing){
  2463.    editMouse(wP,lP);
  2464.   }else{
  2465.    setSelected(selected);
  2466.    if(mouse && !justSelected){
  2467.      Psquare pObj= lineObj(mouse);
  2468.      if(pObj){
  2469.        Psquare oldSel=selected;
  2470.        setSelected(pObj);
  2471.        selected=oldSel;
  2472.      }
  2473.    }
  2474.   }
  2475.  }
  2476.  break;
  2477.  
  2478.  case WM_LBUTTONUP:
  2479.   if(ldown){
  2480.     ReleaseCapture();
  2481.     ldown=false;
  2482.     editPos=0;
  2483.   }
  2484.  break;
  2485.  
  2486.  case WM_RBUTTONUP:
  2487.   if(rdown){
  2488.     ReleaseCapture();
  2489.     rdown=false;
  2490.     editPos=0;
  2491.   }
  2492.  break;
  2493.  
  2494.  case WM_RBUTTONDOWN:{
  2495.   Psquare mouse= SquareXY(LOWORD(lP), HIWORD(lP));
  2496.   if(!mouse) break;
  2497.   if(editing){
  2498.    if(!ldown){
  2499.      rdown=true;
  2500.      SetCapture(hWin);
  2501.    }
  2502.    editMouse(wP,lP);
  2503.   }else{
  2504.    if(replay) break;
  2505.    if(mouse==mover) undo();
  2506.    else{
  2507.     setSelected(0);
  2508.     moveM(mouse);
  2509.    }
  2510.   }
  2511.  }
  2512.  break;
  2513.  
  2514.  case WM_LBUTTONDOWN:{
  2515.   Psquare mouse= SquareXY(LOWORD(lP), HIWORD(lP));
  2516.   if(!mouse) break;
  2517.   if(editing){
  2518.    if(!rdown){
  2519.      ldown=true;
  2520.      SetCapture(hWin);
  2521.    }
  2522.    editMouse(wP,lP);
  2523.   }else{
  2524.    if(replay) break;
  2525.    if(fullUndo()) break;
  2526.    Psquare pObj=0;
  2527.    if(!justSelected) pObj=lineObj(mouse);
  2528.    if(mouse->obj==BM_OBJECT) justSelected=true;
  2529.    if(pObj){
  2530.      movePush(pObj,mouse,true);
  2531.    }else{
  2532.      movePush(selected,mouse,true);
  2533.    }
  2534.    setSelected(mouse);
  2535.    if(finish()) loadLevel(level+1);
  2536.   }
  2537.  }
  2538.  break;
  2539.  
  2540.  case WM_KEYDOWN:
  2541.   if(editing){
  2542.    switch(wP){
  2543.     case VK_RETURN:
  2544.      SendMessage(hWnd,WM_COMMAND,114,0);
  2545.     break;
  2546.     case VK_LEFT:
  2547.     case VK_NUMPAD4:
  2548.      moveE(0);
  2549.     break;
  2550.     case VK_RIGHT:
  2551.     case VK_NUMPAD6:
  2552.      moveE(1);
  2553.     break;
  2554.     case VK_UP:
  2555.     case VK_NUMPAD8:
  2556.      moveE(2);
  2557.     break;
  2558.     case VK_DOWN:
  2559.     case VK_NUMPAD2:
  2560.      moveE(3);
  2561.     break;
  2562.     default:
  2563.      return 0;
  2564.    }
  2565.   }else{
  2566.    if(replay) break;
  2567.    setSelected(0);
  2568.    switch(wP){
  2569.     case VK_LEFT:
  2570.     case VK_NUMPAD4:
  2571.      moveK2(0);
  2572.     break;
  2573.     case VK_RIGHT:
  2574.     case VK_NUMPAD6:
  2575.      moveK2(1);
  2576.     break;
  2577.     case VK_UP:
  2578.     case VK_NUMPAD8:
  2579.      moveK2(2);
  2580.     break;
  2581.     case VK_DOWN:
  2582.     case VK_NUMPAD2:
  2583.      moveK2(3);
  2584.     break;
  2585.     case VK_NUMPAD7:
  2586.      moveK2(4);
  2587.     break;
  2588.     case VK_NUMPAD9:
  2589.      moveK2(5);
  2590.     break;
  2591.     case VK_NUMPAD1:
  2592.      moveK2(6);
  2593.     break;
  2594.     case VK_NUMPAD3:
  2595.      moveK2(7);
  2596.     break;
  2597.     default:
  2598.      return 0;
  2599.    }
  2600.    if(finish()) loadLevel(level+1);
  2601.   }
  2602.  break;
  2603.  
  2604.  case WM_PAINT:{
  2605.   PAINTSTRUCT ps;
  2606.   BeginPaint(hWnd,&ps);
  2607.   repaint();
  2608.   EndPaint(hWnd, &ps);
  2609.  }
  2610.  break;
  2611.  
  2612.  case WM_SIZE:
  2613.   resize();
  2614.  break;
  2615.  
  2616.  case WM_NOTIFY:{
  2617.   LPNMHDR nmhdr = (LPNMHDR) lP;
  2618.   switch(nmhdr->code){
  2619.    case TTN_NEEDTEXT:
  2620.     TOOLTIPTEXT *ttt = (LPTOOLTIPTEXT) lP;
  2621.     int id= ttt->hdr.idFrom+1000;
  2622.     char *s= lng(id,0);
  2623.     if(s){
  2624.       ttt->hinst= NULL;
  2625.       ttt->lpszText= s;
  2626.     }else{
  2627.       //anglick² tooltip je v resource
  2628.       ttt->hinst= inst;
  2629.       ttt->lpszText= MAKEINTRESOURCE(id);
  2630.     }
  2631.    break;
  2632.   }
  2633.  }
  2634.  break;
  2635.  
  2636.  case WM_QUERYENDSESSION:
  2637.   saveAtExit();
  2638.   return TRUE;
  2639.  
  2640.  case WM_CLOSE:
  2641.   DestroyWindow(hWnd);
  2642.   saveAtExit();
  2643.  break;
  2644.  
  2645.  case WM_DESTROY:
  2646.   PostQuitMessage(0);
  2647.  break;
  2648.  
  2649.  default:
  2650.   return DefWindowProc(hWnd, mesg, wP, lP);
  2651.  }
  2652.  return 0;
  2653. }
  2654. //---------------------------------------------------------------------------
  2655. int pascal WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR,int cmdShow)
  2656. {
  2657.  int i;
  2658.  WNDCLASS wc;
  2659.  RECT rc;
  2660.  MSG mesg;
  2661.  static int parts[]={35,110,200,275,335,375,-1};
  2662.  static TBBUTTON tbb[]={
  2663.   {0,106,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2664.   {1,107,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2665.   {10,104,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2666.   {12,109,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2667.   {0,0,0,TBSTYLE_SEP,{0},0},
  2668.   {5,215,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2669.   {4,216,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2670.   {13,112,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2671.   {0,0,0,TBSTYLE_SEP,{0},0},
  2672.   {2,402,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2673.   {3,401,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2674.   {11,403,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2675.   {7,404,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2676.   {0,0,0,TBSTYLE_SEP,{0},0},
  2677.   {9,302,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2678.   {8,301,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2679.   {6,304,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2680.  };
  2681.  static TBBUTTON tbbP[]={
  2682.   {0,201,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2683.   {1,202,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2684.   {2,203,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2685.   {3,204,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2686.   {4,205,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2687.   {5,206,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2688.   {6,207,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2689.   {7,208,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2690.   {8,209,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2691.   {9,210,TBSTATE_ENABLED,TBSTYLE_BUTTON,{0},0},
  2692.  };
  2693.  
  2694.  inst=hInstance;
  2695.  #if _WIN32_IE >= 0x0300
  2696.   INITCOMMONCONTROLSEX iccs;
  2697.   iccs.dwSize= sizeof(INITCOMMONCONTROLSEX);
  2698.   iccs.dwICC= ICC_BAR_CLASSES|ICC_LISTVIEW_CLASSES;
  2699.   InitCommonControlsEx(&iccs);
  2700.  #else
  2701.   InitCommonControls();
  2702.  #endif
  2703.  readini();
  2704.  initLang();
  2705.  //otev°enφ databßze level∙
  2706.  if(initLevels()) return 3;
  2707.  
  2708.  wc.style= CS_OWNDC;
  2709.  wc.lpfnWndProc= MainWndProc;
  2710.  wc.cbClsExtra= 0;
  2711.  wc.cbWndExtra= 0;
  2712.  wc.hInstance= hInstance;
  2713.  wc.hIcon= LoadIcon(hInstance, MAKEINTRESOURCE(1));
  2714.  wc.hCursor= LoadCursor(NULL, IDC_ARROW);
  2715.  wc.hbrBackground= NULL;
  2716.  wc.lpszMenuName= 0;
  2717.  wc.lpszClassName= "SokobanWCLS";
  2718.  if(!hPrevInst && !RegisterClass(&wc)) return 1;
  2719.  
  2720.  hWin = CreateWindow("SokobanWCLS", title,
  2721.    WS_OVERLAPPEDWINDOW-WS_THICKFRAME,
  2722.    CW_USEDEFAULT, CW_USEDEFAULT,
  2723.    20, 20, NULL, NULL, hInstance, NULL);
  2724.  if(!hWin) return 2;
  2725.  dc= GetDC(hWin);
  2726.  hStatus= CreateStatusWindow(WS_CHILD,0,hWin,1);
  2727.  for(i=0; i<sizeA(parts)-1; i++){
  2728.    parts[i]=parts[i]*GetDeviceCaps(dc,LOGPIXELSX)/96;
  2729.  }
  2730.  SendMessage(hStatus, SB_SETPARTS, sizeA(parts),(LPARAM)parts);
  2731.  GetClientRect(hStatus,&rc);
  2732.  statusH= rc.bottom;
  2733.  if(statusBarVisible) ShowWindow(hStatus,SW_SHOW);
  2734.  
  2735.  int n=sizeA(tbb);
  2736.  for(TBBUTTON *u=tbb; u<endA(tbb); u++){
  2737.    if(u->fsStyle==TBSTYLE_SEP) n--;
  2738.  }
  2739.  toolbar = CreateToolbarEx(hWin,
  2740.    WS_CHILD|TBSTYLE_TOOLTIPS|0x800, 2, n,
  2741.    inst, 10, tbb, sizeA(tbb),
  2742.    16,16, 16,15, sizeof(TBBUTTON));
  2743.  if(toolBarVisible) ShowWindow(toolbar,SW_SHOW);
  2744.  toolPlay = CreateToolbarEx(hWin,
  2745.    WS_CHILD|TBSTYLE_TOOLTIPS|0x800, 3, sizeA(tbbP),
  2746.    inst, 11, tbbP, sizeA(tbbP),
  2747.    16,16, 16,15, sizeof(TBBUTTON));
  2748.  bmpdc= CreateCompatibleDC(dc);
  2749.  SetStretchBltMode(bmpdc,HALFTONE);
  2750.  
  2751.  loadSkin(LoadBitmap(hInstance,"SKIN"));
  2752.  if(!*fnskin){
  2753.    getExeDir(fnskin,"skins\\");
  2754.  }else{
  2755.    loadSkin();
  2756.  }
  2757.  if(!bm) return 7;
  2758.  aminmax(level,0,Nlevels-1);
  2759.  prevLevel=level;
  2760.  resetLevel();
  2761.  langChanged();
  2762.  ShowWindow(hWin, cmdShow);
  2763.  UpdateWindow(hWin);
  2764.  initUser();
  2765.  
  2766. #if 0
  2767.  SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);
  2768.  gratulOn=0;
  2769.  for(i=1668; i<Nlevels; i++){
  2770.    loadLevel(i);
  2771.    if(getNobj(levoff[i].offset)<15 && !levoff[i].user.Mmoves){
  2772.      update();
  2773.      UpdateWindow(hWin);
  2774.      findSolution(1);
  2775.      writeini();
  2776.    }
  2777.  }
  2778.  gratulOn=1;
  2779. #endif
  2780.  
  2781.  SetTimer(hWin,128,1000,0);
  2782.  haccel=LoadAccelerators(hInstance, MAKEINTRESOURCE(3));
  2783.  while(GetMessage(&mesg, NULL, 0, 0)==TRUE)
  2784.    if(!TranslateAccelerator(hWin,haccel,&mesg)){
  2785.     TranslateMessage(&mesg);
  2786.     DispatchMessage(&mesg);
  2787.    }
  2788.  hWin=0;
  2789.  DeleteDC(bmpdc);
  2790.  DeleteObject(bm);
  2791.  delete[] board;
  2792.  delete[] distBuf1;
  2793.  delete[] distBuf2;
  2794.  delete[] levoff;
  2795.  delete[] levels;
  2796.  delete[] user;
  2797.  return 0;
  2798. }
  2799. //---------------------------------------------------------------------------
  2800.  
  2801.