/****************************************** THIRSTY NELLAN An 3D Animated Adventure Game for children Copyright (C) 2002 Geoffrey M. Draper This file is part of Thirsty Nellan. Thirsty Nellan is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Thirsty Nellan is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Thirsty Nellan; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA As a special exception, the copyright holder (Geoffrey M. Draper) gives permission to link this program with Qt non-commercial edition, and distribute the resulting executable, without including the source code for the Qt non-commercial edition in the source distribution. (This clause applies only to Windows and MacOS versions of the software.) ******************************************/ #include #include #include #include "mycanvasview.h" #include "infobox.h" #include "gamedata.h" extern GameData *gd; MyCanvasView::MyCanvasView(QCanvas *viewing, QWidget *parent) : QCanvasView(viewing, parent) { setVScrollBarMode(QCanvasView::AlwaysOff); setHScrollBarMode(QCanvasView::AlwaysOff); setFocusPolicy(QWidget::ClickFocus); setFixedHeight(510); connect(viewing, SIGNAL(scoreChanged()), this, SIGNAL(scoreChanged())); connect(viewing, SIGNAL(inventoryItemAdded(int)), this, SIGNAL(inventoryItemAdded(int))); grabKeyboard(); //stepValue = 3; } void MyCanvasView::keyPressEvent( QKeyEvent *k ) { switch (k->key()) { case Qt::Key_Up: moveAvatar(0,-gd->stepValue); gd->avatarDir = 4; //back break; case Qt::Key_Down: moveAvatar(0,gd->stepValue); if (gd->avatarDir == 3) gd->avatarDir = 2; //if right then front-right else gd->avatarDir = 0; //otherwise, front-left break; case Qt::Key_Left: moveAvatar(-gd->stepValue,0); gd->avatarDir = 1; //left break; case Qt::Key_Right: moveAvatar(gd->stepValue,0); gd->avatarDir = 3; //right break; case Qt::Key_Home: moveAvatar(-gd->stepValue,-gd->stepValue); gd->avatarDir = 1; //left break; case Qt::Key_End: moveAvatar(-gd->stepValue,gd->stepValue); gd->avatarDir = 1; //left break; case Qt::Key_PageUp: moveAvatar(gd->stepValue,-gd->stepValue); gd->avatarDir = 3; //right break; case Qt::Key_PageDown: moveAvatar(gd->stepValue,gd->stepValue); gd->avatarDir = 3; //right break; } } void MyCanvasView::contentsMousePressEvent(QMouseEvent *e) { cout << e->x() << " " << e->y() << endl; //cout << "avatar: " << gd->avatar->x() << " " << gd->avatar->y() << endl; int mx = e->x(); int my = e->y(); bool eventHandled = false; gd->pendingAction = gd->DO_NOTHING; QRect *lockedDoorRect, *milkMachine, *bowlMachine, *counterTop; if (!eventHandled) { switch (gd->currentRoom) { case gd->BANK: cout << "You've clicked somewhere in the Bank" << endl; counterTop = new QRect( QPoint(54,82), QPoint(371,503) ); if (gd->currentObject != -1) { if (gd->objectData[gd->currentObject].isTreasure) { if (counterTop->contains(mx,my)) { cout << "you clicked a treasure on the counter top" << endl; if (gd->objectData[gd->currentObject].pointsAwarded == 5) { gd->score += 20; gd->objectData[gd->currentObject].pointsAwarded = 25; emit scoreChanged(); } mx = 407; my = 425; switch (gd->currentObject) { case gd->EGG: gd->pendingAction = gd->DEPOSITING_EGG; break; case gd->JEWEL: gd->pendingAction = gd->DEPOSITING_JEWEL; break; case gd->COIN: gd->pendingAction = gd->DEPOSITING_COIN; break; case gd->VASE: gd->pendingAction = gd->DEPOSITING_VASE; break; default: cout << "this line of code should never be executed" << endl; break; } gd->banker->standUp(); } } else { //if you give a non-treasure to Mr. Klinkoyn eventHandled = true; gd->playSound("audio/Knothankyou.WAV"); ib = new InfoBox("images/squares/banker.png", " Mr. Klinkoyn ti sorride educatamente \n" " e dice, \"No grazie, lo puoi tenere.\" ", parentWidget()); ib->exec(); } } else { QRect *vault = new QRect( QPoint(54,82), QPoint(181,332) ); QRect *brigham = new QRect( QPoint(650,85), QPoint(741,212) ); if ((gd->banker->boundingRect()).contains(mx, my)) { eventHandled = true; if (gd->banker->greeting == false) { gd->playSound("audio/Kwelcome8.WAV"); ib = new InfoBox("images/squares/banker.png", " L'uomo dietro il bancone sembra felice di \n" " vederti. \"Salve! Il mio nome e' Mr. Klinkoyn. \n" " Ho perso quattro tesori da qualche parte in \n" " questa casa. Mi puoi aiutare a ritrovarli?\" ", parentWidget()); ib->exec(); gd->banker->greeting = true; } else { gd->playSound("audio/KAdvenure.WAV"); ib = new InfoBox("images/squares/banker.png", " Mr. Klinkoyn sorride e dice, \"Spero che \n" " tu stia apprezzando l'avventura.\" ", parentWidget()); ib->exec(); } } if (vault->contains(mx,my)) { eventHandled = true; gd->playSound("audio/nvalt.WAV"); ib = new InfoBox(" Quella e' la cassaforte della banca.\n" " Non puoi aprirla in alcun modo. ", parentWidget()); ib->exec(); } if (brigham->contains(mx,my)) { eventHandled = true; gd->playSound("audio/N_brigham.WAV"); ib = new InfoBox( " Alla parete è appeso il ritratto di\n" " un uomo dall'aspetto dignitoso. ", parentWidget()); ib->exec(); } if (!eventHandled && counterTop->contains(mx,my)) { eventHandled = true; gd->playSound("audio/N_points.WAV"); ib = new InfoBox( " Puoi ottenere punti restituindo \n" " dei tesori a Mr. Klinkoyn. ", parentWidget()); ib->exec(); } } break; case gd->GREEN_ROOM: cout << "You've clicked somewhere in the Green Room" << endl; if (gd->currentObject == -1) { QRect *magicChair1 = new QRect( QPoint(486,244), QPoint(589,390) ); QRect *leftChair = new QRect( QPoint(238,242), QPoint(347,388) ); QRect *middleChair = new QRect( QPoint(367,244), QPoint(467,389) ); if (leftChair->contains(mx,my)) { gd->pendingAction = gd->TRYING_LEFT_CHAIR; mx = 289; my = 413; } if (middleChair->contains(mx,my)) { gd->pendingAction = gd->TRYING_MIDDLE_CHAIR; mx = 412; my = 413; } if (magicChair1->contains(mx,my)) { gd->pendingAction = gd->TRYING_RIGHT_CHAIR; mx = 539; my = 413; } } break; case gd->CAT_ROOM: cout << "You've clicked somewhere in the Cat Room" << endl; if (!(gd->objectData[gd->EMPTY_BOWL].location == gd->currentRoom && (gd->objectData[gd->EMPTY_BOWL].largeIcon->boundingRect()).contains(mx, my)) && !(gd->objectData[gd->VASE].location == gd->currentRoom && (gd->objectData[gd->VASE].largeIcon->boundingRect()).contains(mx, my)) && !QRect(QPoint(250, 182), QPoint(353, 354)).contains(mx,my)) { if ((gd->catSprite->boundingRect()).contains(mx, my)) { if (gd->currentObject == gd->WARM_MILK || gd->currentObject == gd->COLD_MILK) { if (gd->currentObject == gd->WARM_MILK) { gd->pendingAction = gd->FEEDING_NELLAN_WARM_MILK; } else { gd->pendingAction = gd->FEEDING_NELLAN_COLD_MILK; } mx = 324; my = 403; } else { eventHandled = true; QString msg = " Nella ti sorride educatamente e dice: \n \""; if (gd->currentObject == -1) { msg += "Spero che tu stia gustando l'avventura.\" "; gd->playSound("audio/neadventure.WAV"); } else { gd->playSound("audio/Nenothanks.WAV"); msg += "No grazie, non mi serve un(a)"; if ((gd->objectData[gd->currentObject].name).left(1) == "E") { msg += "n"; } msg += " " + gd->objectData[gd->currentObject].name + ".\" "; } ib = new InfoBox("images/squares/nellan_hello.png", msg, parentWidget()); ib->exec(); } } } break; case gd->WHITE_ROOM: cout << "You've clicked somewhere in the White Room" << endl; lockedDoorRect = new QRect( QPoint(352,192), QPoint(446,354) ); if (!gd->roomData[gd->OFFICE].visited && lockedDoorRect->contains(mx,my)) { gd->pendingAction = gd->TRYING_LOCKED_DOOR; mx = 395; my = 361; } break; case gd->GOLD_ROOM: cout << "You've clicked somewhere in the Gold Room" << endl; break; case gd->HOT_ROOM: cout << "You've clicked somewhere in the Hot Room" << endl; break; case gd->OFFICE: cout << "You've clicked somewhere in the Office" << endl; if (gd->currentObject == -1) { QRect *magicChair2 = new QRect( QPoint(450,251), QPoint(561,431) ); QRect *trs80 = new QRect( QPoint(280,217), QPoint(421,286) ); QRect *officeWindow = new QRect( QPoint(412,97), QPoint(550,217) ); QRect *bookShelf = new QRect( QPoint(574,84), QPoint(790,407) ); if (gd->objectData[gd->MAGAZINE].largeIcon->boundingRect().contains(mx, my)) { gd->pendingAction = gd->PICKING_UP_MAGAZINE; mx = 279; my = 460; } if (magicChair2->contains(mx,my)) { cout << "you clicked on the magic chair" << endl; gd->pendingAction = gd->TRYING_OFFICE_CHAIR; mx = 487; my = 446; } if (trs80->contains(mx,my)) { eventHandled = true; gd->playSound("audio/ncomputer.WAV"); ib = new InfoBox( " Sulla scrivania c'è un computer \n" " TRS-80 Modello 3 molto vecchio. Non \n" " vedi uno di questi relitti da un \n" " sacco di tempo. Peccato che non \n" " sembri più in grado di funzionare. ", parentWidget()); ib->exec(); } if (officeWindow->contains(mx,my)) { eventHandled = true; gd->playSound("audio/Nwindow.WAV"); ib = new InfoBox( " La finestra polverosa offre l'unica \n" " visuale del mondo esterno. \n" " Però non riesci a distinguere \n" " alcun dettaglio.", parentWidget()); ib->exec(); } if (bookShelf->contains(mx,my)) { eventHandled = true; gd->playSound("audio/Nbooks.WAV"); ib = new InfoBox(" Lo scaffale contiene parecchi volumi \n" " grossi e pesanti. Però nessuno di questi \n" " sembra molto interessante.", parentWidget()); ib->exec(); } } break; case gd->MILK_ROOM: cout << "You've clicked somewhere in the Milk Room" << endl; if ((gd->objectData[gd->COLD_MILK].location == gd->currentRoom && (gd->objectData[gd->COLD_MILK].largeIcon->boundingRect()).contains(mx,my))) { mx = 263; my = 434; } else { milkMachine = new QRect( QPoint(176,206), QPoint(313,418) ); if (milkMachine->contains(mx,my)) { if (gd->currentObject == gd->EMPTY_BOWL) { gd->pendingAction = gd->FILLING_BOWL; mx = 341; my = 413; } if (gd->currentObject == gd->COLD_MILK || gd->currentObject == gd->WARM_MILK) { eventHandled = true; gd->playSound("audio/bowl_full_of_milk.WAV"); ib = new InfoBox(" La tua tazza è gia piena di latte. \n" " Devi prima svuotarla se vuoi prendere \n" " ancora del latte.", parentWidget()); ib->exec(); } if (gd->currentObject == -1) { eventHandled = true; gd->playSound("audio/Nmilkcooler.WAV"); ib = new InfoBox(" Questo è un refrigeratore per latte. \n" " Se hai una tazza vuota, puoi riempirla \n" " di latte da qui.", parentWidget()); ib->exec(); } } } if (gd->currentObject == gd->CHULA) { if (!gd->carrotsEaten && (gd->carrotSprite->boundingRect()).contains(mx, my)) { eventHandled = true; gd->playSound("audio/eat_carrots.WAV"); ib = new InfoBox("images/squares/chula_carrot.png", " Celeste balza a terra e divora\n" " avidamente l'enorme mucchio di\n" " carote. Schiocca la lingua e dice,\n" " \"Grazie, amico! Che spuntino \n" " delizioso!\" Poi scappa in un\n" " angolino per riposare.", parentWidget()); ib->exec(); gd->carrotsEaten = true; gd->roomData[gd->MILK_ROOM].perimeter->setPoint(2, 710-40, 447); //east door, right corner gd->roomData[gd->MILK_ROOM].perimeter->setPoint(3, 684-40, 410); //east door, center gd->objectData[gd->CHULA].location = gd->MILK_ROOM; gd->objectData[gd->CHULA].largeIcon->move(130,413); gd->objectData[gd->CHULA].largeIcon->setZ(-1); emit inventoryItemRemoved(gd->CHULA); } } if (gd->currentObject == -1) { QRect *cowPainting = new QRect( QPoint(405,147), QPoint(536,243) ); if (cowPainting->contains(mx, my)) { eventHandled = true; gd->playSound("audio/Moo.WAV"); ib = new InfoBox(" \"MOO!\" ", parentWidget()); ib->exec(); } if (!gd->carrotsEaten && (gd->carrotSprite->boundingRect()).contains(mx, my)) { eventHandled = true; gd->playSound("audio/Ncarrotsblocking.WAV"); ib = new InfoBox(" Wow! C'è un enorme mucchio di carote \n" " che blocca il passaggio. E' troppo \n" " pesante da spostare, troppo alto da \n" " scalare, ed è troppo grande perché tu\n" " possa mangiarlo tutto da solo. \n" " Cosa pensi che dovresti fare? \n", parentWidget()); ib->exec(); } } break; case gd->STORE: cout << "You've clicked somewhere in the Store" << endl; if (!(gd->objectData[gd->EMPTY_BOWL].location == gd->currentRoom && (gd->objectData[gd->EMPTY_BOWL].largeIcon->boundingRect()).contains(mx,my))) { bowlMachine = new QRect( QPoint(386,135), QPoint(603,385) ); if (bowlMachine->contains(mx,my)) { if (gd->currentObject == -1) { eventHandled = true; QString msg = " C'è un distributore di tazze. \n"; if (gd->objectData[gd->COUPON].location == -2) { gd->playSound("audio/Nvending.WAV"); msg += " Non sembra funzionare \n al momento. "; } else { gd->playSound("audio/Ncoupond.WAV"); msg += " Se hai una tessera, potresti inserire \n" " la tessera nella macchina per \n" " ricevere una bella tazza nuova."; } ib = new InfoBox(msg, parentWidget()); ib->exec(); } if (gd->currentObject == gd->COUPON) { gd->pendingAction = gd->DEPOSITING_COUPON; mx = 393; my = 414; } } } else { //user clicked on bowl in machine, walk him to base of machine mx = 465; my = 419; gd->pendingAction = gd->DO_NOTHING; } break; } } if (!eventHandled) { if ((gd->avatar->boundingRect()).contains(mx, my)) { if (gd->currentObject == gd->COLD_MILK || gd->currentObject == gd->WARM_MILK) { eventHandled = true; gd->playSound("audio/Ncalcium.WAV"); ib = new InfoBox(" Sluuuuurrp! Ti sei bevuto tutto il latte\n" " della tazza. Buon per te! \n" " In questo modo hai ricevuto la tua dose\n" " giornaliera minina di calcio, oggi! ", parentWidget()); ib->exec(); emit inventoryItemRemoved(gd->currentObject); emit inventoryItemAdded(gd->EMPTY_BOWL); gd->objectData[gd->currentObject].location = -2; } } } //catch all unexpected actions if (!eventHandled) { if (gd->currentObject != -1 && gd->pendingAction == gd->DO_NOTHING) { gd->playSound("audio/Nnogoodthere.WAV"); ib = new InfoBox((" Dubito che usare un(a)" + gd->objectData[gd->currentObject].name + " \n qui possa essere utile. "), parentWidget()); ib->exec(); } else { //did user click on the floor? if (isInsidePerimeter(mx, my, gd->roomData[gd->currentRoom].largePerimeter)) { int rise, run; double x_velocity, y_velocity; eventHandled = true; gd->avatarDestination = new QPoint(mx, my); //did the user click in a doorway? for (int i=0; i < gd->roomData[gd->currentRoom].numPassages; i++) { if (isInsidePerimeter(mx, my, (gd->roomData[gd->currentRoom].passageway[i]).largePerimeter)) { gd->avatarDestination->setX((gd->roomData[gd->currentRoom].passageway[i]).target->x()); gd->avatarDestination->setY((gd->roomData[gd->currentRoom].passageway[i]).target->y()); } } rise = gd->avatarDestination->y() - (int)gd->avatar->y(); run = gd->avatarDestination->x() - (int)gd->avatar->x(); x_velocity = y_velocity = gd->stepValue; //cout << "rise: " << rise << " run: " << run << endl; //workaround to avoid "disappearing avatar" bug if (sqrt(abs(rise*rise) + abs(run*run)) < (double)5) { //cout << "don't hide on me now!" << endl; x_velocity = y_velocity = 0; } else { if (abs(rise) >= abs(run)) { //go mostly vertical if (rise < 0) { y_velocity *= -1; gd->avatarDir = 4; } else { gd->avatarDir = (run < 0 ? 0 : 2); } if (rise != 0) x_velocity = run / fabs(rise / gd->stepValue); } else { //go mostly horizontal if (run < 0) { x_velocity *= -1; gd->avatarDir = 1;//left } else { gd->avatarDir = 3;//right } if (run != 0) y_velocity = rise / fabs(run / gd->stepValue); } } gd->avatar->setVelocity(x_velocity, y_velocity); } } } emit currentObjectDropped(); } void MyCanvasView::wheelEvent ( QWheelEvent * e ) { e->ignore(); } void MyCanvasView::moveAvatar(int x, int y) { int xx = (int)gd->avatar->xVelocity(); int yy = (int)gd->avatar->yVelocity(); if (xx == x && yy == y) { gd->avatar->setVelocity(0,0); } else { gd->avatar->setVelocity(x,y); } } int MyCanvasView::isInsidePerimeter(int xx, int yy, QPointArray *p) { //adapted from http://astronomy.swin.edu.au/pbourke/geometry/insidepoly/ //by Randolph Franklin int i, j, c = 0; int npol = p->size(); QPoint *pi, *pj; //what, you expected this code to make SENSE ? for (i = 0, j = npol-1; i < npol; j = i++) { pi = new QPoint(p->point(i)); pj = new QPoint(p->point(j)); if ((((pi->y() <= yy) && (yy < pj->y() )) || ((pj->y() <= yy) && (yy < pi->y() ))) && (xx < (pj->x() - pi->x()) * (yy - pi->y() ) / (pj->y() - pi->y() ) + pi->x() )) c = !c; } return c; }