/**************/ /* autot.c */ /**************************************************************************** PROGRAM: autot.c Windows: Win 3.1 & WIN32 PURPOSE: Aliohjelmakirjasto liikkuvien autojen (tms.) piirtämiseksi näytölle. - Autoja voi pysäköidä ja laittaa jälleen liikkumaan napauttamalla autoa hiiren vasemmalla näppäimellä. Hiiren kursori muuttuu ristinuoleksi aina kun kursori on auton kohdalla. - Oikealla hiiren näppäimellä tuhotaan koko auto. - Kuvien ei tietysti tarvitse oikeasti olla autoja, vaan ne voivat olla mitä tahansa (suoraviivaisesti) liikkuviksi tarkoitettuja objekteja. AUTHOR: Vesa Lappalainen 20.10.1992 USAGE: Käyttäjän pitää 0) Liittää projektiin ALI\autot.c, ALI\mjonot.c ja pääohjelmaan #include "autot.h" 1) liittää .RC tiedostoonsa haluttujen autojen bittikartat. Auton bittikarttaan on piirretty oikealle kulkevan auton kuva siten, että läpinäkyviksi halutut kohdat on valkoisella. 2) tehdä .INI tiedosto, jossa on autojen koordinaatit, nimet ja nopeudet. (ks. alusta_autot()) 3) autojen alustamiseksi ohjelmassa kutsua sopivassa kohti funktiota alusta_autot(NULL,hWnd,ininame); Funktiota voidaan kutsua myös uudelleen autojen lisäämiseksi esimerkiksi eri ininame-tiedostosta. 4) ohjelman lopuksi (tai muulloin tarvittaessa) poistaa autot kutsulla tuhoa_autot(); (tätä ei ole enää pakko käyttää, koska Windows huolehtii tästä automaattisesti lähettämällä jokaisella autolle WM_DESTROY-viestin, jolla kukin auto poistetaan) ============ Aliohjelmat: (autot.c, autot.h) ============ alusta_autot() - alustetaan autot tuhoa_autot() - tuhotaan autot ============================= Aliohjelmien tarkempi kuvaus: ============================= --------------------------------------------------------------------- int alusta_autot(HINSTANCE hInst, HINSTANCE hPrev,HWND hWnd, char *inifile) --------------------------------------------------------------------- Funktiolla alustetaan autot. Autot on liitetty ohjelman resursseihin bittikarttoina ja niiden ominaisuudet on määrätty .INI -tiedostossa. Funktiota voidaán kutsua useita kertoja, jolloin saadaan mukaan eri .INI-tiedostoissa olevia autoja. HINSTANCE hInst esiintymän kahva, josta autojen kuvat löytyvät. Jos NULL, otetaan se esiintymä, joka luonut ikkunan hWnd. Molemmat hInst ja hWnd eivät voi olla NULL! HWND hWnd: ikkunan kahva, johon autot tulevat liikkumaan Toimii jotenkin, jos = NULL, jolloin autot liikkuvat kaikkien ohjelmien päällä ja koko ruudun alalla. char *inifile: .INI-tiedoston nimi, josta autoja etsitään, ei .INI tarkenninta mukana. .INI-tiedoston muoto on: ------------------------ [Autot] Auto0=HAUTO,1,0,10,200 Auto1=KAUTO,0,100,20,300,10,1 Auto2=HAUTO,0,70,30,100 Yhden auton parametrit ovat: (0 aina oletus) ---------------------------- 1 = Resurssitiedostossa olevan bittikartan nimi 2 = Auto peittää koko taustansa ( 0 = näkyy läpi ) 1 = nopeampi liikkumaan, muttei toimi jollei kuva ole koko taustansa kokoinen! 3 = y-koordinaatti näytöllä ( 0 = ajaa pohjalla ) 4 = auton hyppäys aikavälissä pisteinä ( 0 = 10 pistettä ) 5 = timerin jakso millisekuntteina ( 0 = 100 ms ) 6 = auton alkuperäinen x-paikka ( 0 = ikkunan puoliväli) 7 = onko auto päällä vai alla ( 0 = alla ) Autoja tulee niin monta kuin viimeinen juokseva Auto? ilmoittaa (+1), numerointi täytyy aloittaa 0:sta. .RC-tiedostossa: ---------------- HAUTO BITMAP "hauto.bmp" KAUTO BITMAP "kauto.bmp" ---------------------- void tuhoa_autot(void) ---------------------- Tuhoaa kaikki ohjelmaan liitetyt autot. Tätä on muistettava kutsua, jotta kaikki autoille tehdyt varaukset saadaan poistetuksi. Aliohjelman kutsuminen useampaan kertaan ei haitaa, samoin kuin ei sekään, vaikka kaikki autot jo olisi poistettu hiiren oikealla näppäimellä. ***************************************************************************** ** ** Toteutus: ** ========= ** ** Autot on toteutettu ikkunoina, koska muuten jouduttaisiin aina auton ** siirtämiseksi lukemaan tausta talteen. Kuitenkin Windows saattaa muuttaa ** taustaa ja näin autoja pitäisi hävittää näytöstä ennen taustaa ** muuttavia toimenpiteitä ja tämä on melko mahdotonta. Taustaa nimittäin ** muuttaa menut, muut ikkunat, muut mahdollisesti liikkuvat autot jne. ** ** Auton oleminen päällimmäisenä tarkoittaa sitä, että se on hiirelle ** ensin löytyvä. Kuitenkin kuvana päällä oleva (TOP) kulkee muiden ** ikkunoiden alitse??? ** ** Kun kursori tulee auton kohdalle, muutetaan se ristinuoleksi. ** ** **18.10.1992/vl: ** Toistaiseksi autojen piirtäminen hieman "nykii". ** Voitaisiinko ikoneilla ehkä hoitaa siistimmin??? ** **8.11.1992 ** Toiminta ei aivan kunnollista, mikäli hwnd==NULL. Auton ** "tausta" ei korjaannu kunnolla! CS_SAVEBITS luomisessa ei auta! ** **20.8.1994/vl: ** WM_DESTROY-toimimaan, jottei tuhoa_autot olisi välttämätön ** ****************************************************************************/ #include #include #include #include #include "mjonot.h" #include "autot.h" #define WAutoWClass "WAutoWClass" /***************************************************************************/ typedef struct { /* Yhden auton bittikartojen kahvat */ HBITMAP hAnd; /* AND -maski (mustavalkoinen) */ HBITMAP hOr; /* OR -maski (mahd. värillinen) */ } MaskiTyyppi; /***************************************************************************/ /* .INI -merkityt kohdat voi alustaa .INI-tiedostolla */ typedef struct { /* Auton ylläpitämiseksi tarvittavat tiedot: */ char nimi[20]; /* Bittikuvan nimi. .INI */ int korkeus; /* Auton yläreunan y-koordinaatti, 0 on pohj. .INI */ int vauhti; /* x-siirtymä kerrallaan .INI */ int aikavali; /* Siirtymien aikaväli ms .INI */ int paikka; /* Auton x-koordinaatti .INI */ int suunta; /* +/- vauhti kulkusuunnan mukaan */ int xkoko; /* Bittikartan x-koko (Selvitetään bittikartasta) */ int ykoko; /* Bittikartan y-koko */ int xmin; /* Auton liikkeen pienen x-koordinaatti */ int xmax; /* Vastaava suurin x-koordinaatti (ikkunan koko määr) */ int kopio; /* Onko bittikartat kopioita jostakin muusta */ MaskiTyyppi AutoO; /* Oikealle kulkevan auton bittimaskit */ MaskiTyyppi AutoV; /* Vasemmalle kulkevan auton bittimaskit */ MaskiTyyppi Nyt; /* Kulkusuunnan mukaan joko AutoO tai AutoV */ int alustettu; /* Onko tietorakenne alustettu (static -> 0 eli ei) */ int nakyvissa; /* Onko auto näkyvissä (nykyinen versio ei käytä) */ int pysakoity; /* Onko auto pysäköity vai ei (oletus ei). */ HWND hwnd; /* Autoa kuvaavan ikkunan kahva. */ HWND ohitus; /* Kulkeeko auto päällä vai pohjalla .INI */ HWND ParenthWnd; /* Isäikkunan kahva. */ HINSTANCE hInstance;/* Esiintymä, jossa auton ikkuna luotu */ int kokokuva; /* Auto täyttää koko ikkunansa,helppo siirtää .INI */ } AutoTyyppi; /***************************************************************************/ /* Tiedoston globaalit muuttujat: */ #define MAX_AUTOJA 6 static AutoTyyppi Autot[MAX_AUTOJA]; static int AUTOJA=0; /***************************************************************************/ /***************************************************************************/ static int is_class(HINSTANCE hInst, char *name) /* Palauttaa 1 jos name-niminen luokka on olemassa */ { WNDCLASS wc; wc.style = NULL; wc.lpfnWndProc = (WNDPROC)is_class; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = name; if ( !RegisterClass(&wc) ) return 1; UnregisterClass(name,hInst); return 0; } /***************************************************************************/ static void LaitaMaskikartat(HDC hDC,HBITMAP hAnd,HBITMAP hOr,int x,int y) /* Piirretään AND ja OR bittikartat alkaen paikasta x,y */ { HDC hDCBit = CreateCompatibleDC(hDC); BITMAP bm; HGDIOBJ hOld = SelectObject(hDCBit,hAnd); GetObject(hAnd,sizeof(bm),&bm); BitBlt(hDC,x,y,bm.bmWidth,bm.bmHeight,hDCBit,0,0,SRCAND); SelectObject(hDCBit,hOr); BitBlt(hDC,x,y,bm.bmWidth,bm.bmHeight,hDCBit,0,0,SRCPAINT); SelectObject(hDCBit,hOld); DeleteDC(hDCBit); } /***************************************************************************/ static int alusta_auto(HWND hWnd,AutoTyyppi *a) /* ** Alkuperäinen oikealle kulkeva auton bittikuva ladataan ja siitä tehdään ** vastaava vasemmalle kulkeva kuva peilikuvana. ** Jotta näytölle saataisiin vain auton kuva, eikä valkoista laatikkoa auton ** ympärille, tehdään piirtovaiheessa siten, että kuvaan tehdään ** autonmuotoinen musta reikä, johon auto sitten piirretään OR- ** operaatiolla. Alkuperäisestä, autosta pitää OR-operaatiota varten ** laittaa reunat mustaksi. ** Autonmuotoinen musta reikä saadaan maskilla, johon on laitettu mustaa ** kaikkiin auton värillisiin kohtiin ja valkoista kaikkialle reunoille. ** Tämä maski saadaan helpoiten muuttamalla värillinen auto mv:ksi. ** Värillisen auton reunat saadaan sitten mustaksi tekemällä INVERT ** kaikille reunapisteille edellä saadun maskin avulla. ** ** Siis auton liikuttelua varten tarvitaan 4 maskia. ** Nyt-maski on kopio joko oikean tai vasemman suunnan autoista ** liikesuunnan mukaan. ** ** Lisäksi alustetaan muut tarpeelliset tiedot. Jos jonkin arvo on ** alunperin 0, laitetaan siihen oletusarvo. ** ** Tilan säästämiseksi ei saman nimiselle autolle luoda enää uutta ** bittikarttaa, vaan otetaan kopio vanhasta. Mikäli otetaan kopio ** pitää ohjelmoijan pitää huolta, että jos varsinainen auto tuhotaan ** ei kopioitakaan enää käytetä, tai jos jostakin autosta on kopio ** ei sitä saa tuhota, ennenkuin kopiot on poistetu käytöstä. ** ** Mikäli auton nimellä olevaa bittikarttaa ei löydy lainkaan, ** jätetään vastaava auto alustamatta */ { if ( !a ) return 1; if ( a->alustettu ) return 0; { int i; RECT rc; HDC hDC = GetDC(hWnd); HDC hDCBit1 = CreateCompatibleDC(hDC); /* Apu muisti-DC:t piirtelyyn */ HDC hDCBit2 = CreateCompatibleDC(hDC); HGDIOBJ hOld1,hOld2; /* Ja niiden vanhat "työkalut" */ BITMAP bm; a->ParenthWnd=hWnd; if ( !hWnd ) hWnd = GetDesktopWindow(); GetClientRect(hWnd,&rc); for (i=0; inimi) == 0 ) ) { a->kopio = 1; a->AutoO = Autot[i].AutoO; a->AutoV = Autot[i].AutoV; a->xkoko = Autot[i].xkoko; a->ykoko = Autot[i].ykoko; goto jatka_alustusta; } /* Oikealle ja vasemmalle liikkuvat värilliset autot */ a->AutoO.hOr = LoadBitmap(a->hInstance,a->nimi); if ( !a->AutoO.hOr ) goto poista_DCt; GetObject(a->AutoO.hOr,sizeof(bm),&bm); a->xkoko = bm.bmWidth; a->ykoko = bm.bmHeight; a->AutoV.hOr = CreateCompatibleBitmap(hDC,a->xkoko,a->ykoko); /* Mustavalkoiset maskiautot (bitmap on mustavalk. jos CCB muisti DC:stä */ /* jossa ei ole ennestään bitmappia!) */ a->AutoO.hAnd = CreateCompatibleBitmap(hDCBit1,a->xkoko,a->ykoko); a->AutoV.hAnd = CreateCompatibleBitmap(hDCBit2,a->xkoko,a->ykoko); hOld1 = SelectObject(hDCBit1,a->AutoO.hOr); hOld2 = SelectObject(hDCBit2,a->AutoO.hAnd); /* Maskiauto, jossa vain mustaa autossa ja valkoista reunoilla */ BitBlt(hDCBit2,0,0,a->xkoko,a->ykoko,hDCBit1,0,0,SRCCOPY); /* Alkuperäisestä autosta reunat mustaksi maskiauton avulla */ BitBlt(hDCBit1,0,0,a->xkoko,a->ykoko,hDCBit2,0,0,SRCINVERT); /* Vasemmalle liikkuva auto tehdään oikealle liikkuvasta autosta */ SelectObject(hDCBit2,a->AutoV.hOr); /* Tehdään auton peilikuva bittikarttaan AutoV.hOr */ StretchBlt(hDCBit2,0,0,a->xkoko,a->ykoko, hDCBit1,a->xkoko-1,0,-a->xkoko,a->ykoko,SRCCOPY); /* Mustavalkoinen vasemmalle menevä maskiauto */ SelectObject(hDCBit1,a->AutoV.hAnd); SelectObject(hDCBit2,a->AutoO.hAnd); /* Tehdään mustavalkoisen auton peilikuva bittikarttaan AutoV.hAnd */ StretchBlt(hDCBit1,0,0,a->xkoko,a->ykoko, hDCBit2,a->xkoko-1,0,-a->xkoko,a->ykoko,SRCCOPY); /* Poistetaan turhat DC:t */ SelectObject(hDCBit2,hOld2); SelectObject(hDCBit1,hOld1); jatka_alustusta: a->Nyt = a->AutoO; if ( !a->vauhti ) a->vauhti = 10; if ( !a->aikavali ) a->aikavali = 100; if ( !a->xmax ) a->xmax = rc.right - a->xkoko; if ( !a->korkeus ) a->korkeus = rc.bottom - a->ykoko; a->suunta = a->vauhti; a->paikka = a->xmax/2; a->hwnd = CreateWindow(WAutoWClass,a->nimi, WS_CHILD | WS_VISIBLE, a->paikka,a->korkeus,a->xkoko,a->ykoko, hWnd,NULL,a->hInstance,NULL); SetTimer(a->hwnd,1,a->aikavali,NULL); a->nakyvissa = 0; a->alustettu = 1; poista_DCt: DeleteDC(hDCBit2); DeleteDC(hDCBit1); ReleaseDC(hWnd,hDC); return 0; } } /***************************************************************************/ static int siirra_auto(AutoTyyppi *a) { int suunta_vaihtui = 0; if ( !a ) return 1; if ( !a->alustettu || a->pysakoity ) return 1; a->paikka += a->suunta; if ( a->paikka > a->xmax ) { /* Valitaan auto vasemmalle */ a->paikka = a->xmax; a->suunta = -a->vauhti; a->Nyt = a->AutoV; suunta_vaihtui = 1; } if ( a->paikka < a->xmin ) { /* Valitaan auto oikealle */ a->paikka = a->xmin; a->suunta = a->vauhti; a->Nyt = a->AutoO; suunta_vaihtui = 1; } if ( a->ParenthWnd ) { if ( !a->kokokuva || suunta_vaihtui ) InvalidateRect(a->hwnd,NULL,FALSE); SetWindowPos(a->hwnd,a->ohitus,a->paikka,a->korkeus,a->xkoko,a->ykoko, SWP_NOSIZE ); } else { /* Jollei isäikkunaa, eli liikkuu koko ruudun alueella */ // InvalidateRect(a->hwnd,NULL,TRUE); /* Ei tunnu tarvitsevan */ SetWindowPos(a->hwnd,a->ohitus,a->paikka,a->korkeus,a->xkoko,a->ykoko, SWP_HIDEWINDOW ); SetWindowPos(a->hwnd,a->ohitus,a->paikka,a->korkeus,a->xkoko,a->ykoko, SWP_NOSIZE ); SetWindowPos(a->hwnd,a->ohitus,a->paikka,a->korkeus,a->xkoko,a->ykoko, SWP_SHOWWINDOW ); } return 0; } /***************************************************************************/ static int putsaa_auto(AutoTyyppi *a) { int i; #define TUHOA_BIT(b) \ { DeleteObject(a->b.hAnd); \ DeleteObject(a->b.hOr); } if ( !a ) return 1; if ( !a->alustettu ) return 1; KillTimer(a->hwnd,1); a->alustettu = 0; if ( a->kopio == 0 ) { for (i=0;iAutoV.hOr ) { Autot[i].kopio=0; return 0; } TUHOA_BIT(AutoV); TUHOA_BIT(AutoO); } return 0; #undef TUHOA_BIT } /***************************************************************************/ static int tuhoa_auto(AutoTyyppi *a) { HDC hDC; if ( !a ) return 1; putsaa_auto(a); a->kopio = 0; hDC=GetDC(a->hwnd); if (hDC) { /* DestroyWindow vain mikäli sitä ei ole jo tehty */ ReleaseDC(a->hwnd,hDC); DestroyWindow(a->hwnd); } return 0; } /***************************************************************************/ void tuhoa_autot(void) { int i; for (i=AUTOJA-1;i>=0;i--) tuhoa_auto(&Autot[i]); } /***************************************************************************/ static AutoTyyppi *EtsiAuto(HWND hwnd) /* Etsitään auto, joka on yhdistetty ikkunaan hwnd */ { int i; for (i=0; iNyt.hAnd,a->Nyt.hOr,0,0); // TextOut(ps.hdc,0,0,"Auto",4); } EndPaint(hWnd,&ps); return NULL; case WM_LBUTTONDOWN: if ( (a=EtsiAuto(hWnd)) == NULL ) break; a->pysakoity = !a->pysakoity; return NULL; case WM_RBUTTONDOWN: if ( (a=EtsiAuto(hWnd)) == NULL ) break; tuhoa_auto(a); /* Koe jos yksi auto kerrallaan tuhotaan */ return NULL; case WM_TIMER: if ( (a = EtsiAuto(hWnd)) == NULL ) break; if ( IsIconic(a->ParenthWnd) ) return NULL; siirra_auto(a); return NULL; case WM_DESTROY: /* Tämä ei ollutkaan tervellinen jos oli TuhoaAuto */ if ( (a = EtsiAuto(hWnd)) == NULL ) break; putsaa_auto(a); return 0; default: /* Antaa Windowsin käsitellä muut */ break; } return (DefWindowProc(hWnd, message, wParam, lParam)); } /***************************************************************************/ int alusta_autot(HINSTANCE hInst, HWND hWnd, const char *inifile) /* Luetaan autojen ominaisuudet .INI-tiedostosta. Palautetaan löydettyjen ** autojen lukumäärä. */ { int i,lkm,ohitus; static int ei_alustettu=1; char ininame[40],pname[20],s[80],*p; WNDCLASS wc; /* Ikkunaluokka */ if ( !hInst ) { if ( !hWnd ) return 0; /* Molemmat eivät saa olla NULL! */ hInst = GetWindowInstance(hWnd); } if (ei_alustettu && !is_class(hInst,WAutoWClass)){ /* Onko jo luotu? */ wc.style = NULL; wc.lpfnWndProc = AutoWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_SIZE); wc.hbrBackground = GetStockObject(HOLLOW_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = WAutoWClass; if (!RegisterClass(&wc)) return 0; ei_alustettu = 0; } kopioi_jono(N_S(ininame),inifile); p = strrchr(ininame,'.'); if ( p ) *p = 0; liita_jono(N_S(ininame),".ini"); /* Tehdään .INI-tiedoston nimi */ for (i=AUTOJA,lkm=0;i