1 /* FloboPuyo
2  * Copyright (C) 2004
3  *   Florent Boudet        <flobo@ios-software.com>,
4  *   Jean-Christophe Hoelt <jeko@ios-software.com>,
5  *   Guillaume Borios      <gyom@ios-software.com>
6  *
7  * iOS Software <http://www.ios-software.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  *
24  */
25 
26 #include <stdlib.h>
27 #include <math.h>
28 #include <unistd.h>
29 #include "PuyoView.h"
30 #include "PuyoAnimations.h"
31 #include "AnimatedPuyo.h"
32 #include "PuyoGame.h"
33 #include "audio.h"
34 #include "IosImgProcess.h"
35 #include "HiScores.h"
36 #include "PuyoDoomMelt.h"
37 
38 #include "SDL_Painter.h"
39 SDL_Painter painter;
40 
41 SDL_Surface *display;
42 IIM_Surface *background, *fallingBlue, *fallingRed, *fallingGreen, *fallingViolet, *fallingYellow, *neutral;
43 IIM_Surface *bigNeutral;
44 IIM_Surface *puyoEyes;
45 IIM_Surface *puyoFaces[5][16];
46 IIM_Surface *puyoShadow;
47 
48 
49 
50 static char PuyoGroupImageIndex[2][2][2][2] =
51 { {  // empty bottom
52 {  // empty right
53 {  // empty top
54 	0, // empty left
55 	4, // full left
56 },
57 {  // full top
58 	3, // empty left
59 	10, // full left
60 }
61 },
62 {  // full right
63 	{  // empty top
64         2, // empty left
65         9, // full left
66 	},
67 	{  // full top
68         8, // empty left
69         14, // full left
70 	}
71 }
72 },
73 {  // full bottom
74     {  // empty right
75 	{  // empty top
76         1, // empty left
77         7, // full left
78 	},
79 	{  // full top
80         6, // empty left
81         13, // full left
82 	}
83     },
84     {  // full right
85 	{  // empty top
86         5, // empty left
87         12, // full left
88 	},
89 	{  // full top
90         11, // empty left
91         15, // full left
92 	}
93     }
94 }
95 };
96 
97 
98 
PuyoView(PuyoRandomSystem * attachedRandom,int xOffset,int yOffset,int nXOffset,int nYOffset)99 PuyoView::PuyoView(PuyoRandomSystem *attachedRandom, int xOffset, int yOffset, int nXOffset, int nYOffset)
100 :attachedPuyoFactory(this), attachedPainter(painter)
101 {
102 	attachedGame = new PuyoGame(attachedRandom, &attachedPuyoFactory);
103     attachedGame->setDelegate(this);
104 
105 	this->xOffset = xOffset;
106 	this->yOffset = yOffset - TSIZE;
107 	this->nXOffset = nXOffset;
108 	this->nYOffset = nYOffset;
109 	gameRunning = true;
110 	enemyGame = NULL;
111     skippedCycle = false;
112     cycleAllowance = 0;
113 }
114 
setEnemyGame(PuyoGame * enemyGame)115 void PuyoView::setEnemyGame(PuyoGame *enemyGame)
116 {
117 	this->enemyGame = enemyGame;
118 }
119 
getSurfaceForState(PuyoState state)120 IIM_Surface *PuyoView::getSurfaceForState(PuyoState state)
121 {
122 	switch (state) {
123 		case PUYO_BLUE:
124 			return fallingBlue;
125 			break;
126 		case PUYO_RED:
127 			return fallingRed;
128 			break;
129 		case PUYO_GREEN:
130 			return fallingGreen;
131 			break;
132 		case PUYO_VIOLET:
133 			return fallingViolet;
134 			break;
135 		case PUYO_YELLOW:
136 			return fallingYellow;
137 			break;
138 		case PUYO_FALLINGBLUE:
139 			return fallingBlue;
140 			break;
141 		case PUYO_FALLINGRED:
142 			return fallingRed;
143 			break;
144 		case PUYO_FALLINGGREEN:
145 			return fallingGreen;
146 			break;
147 		case PUYO_FALLINGVIOLET:
148 			return fallingViolet;
149 			break;
150 		case PUYO_FALLINGYELLOW:
151 			return fallingYellow;
152 			break;
153 		case PUYO_NEUTRAL:
154 			return neutral;
155 			break;
156 		default:
157 			return NULL;
158 			break;
159 	}
160 }
161 
getSurfaceForPuyo(PuyoPuyo * puyo)162 IIM_Surface *PuyoView::getSurfaceForPuyo(PuyoPuyo *puyo)
163 {
164     int i = puyo->getPuyoX();
165     int j = puyo->getPuyoY();
166     PuyoState currentPuyoState = puyo->getPuyoState();
167     AnimatedPuyo *down  = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j+1));
168     AnimatedPuyo *right = (AnimatedPuyo *)(attachedGame->getPuyoAt(i+1, j));
169     AnimatedPuyo *up    = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j-1));
170     AnimatedPuyo *left  = (AnimatedPuyo *)(attachedGame->getPuyoAt(i-1, j));
171 
172     PuyoState downState = (down == NULL)   || (down->isRenderingAnimation())  ? PUYO_EMPTY  : down->getPuyoState();
173     PuyoState rightState = (right == NULL) || (right->isRenderingAnimation()) ? PUYO_EMPTY  : right->getPuyoState();
174     PuyoState upState = (up == NULL)       || (up->isRenderingAnimation())    ? PUYO_EMPTY  : up->getPuyoState();
175     PuyoState leftState = (left == NULL)   || (left->isRenderingAnimation())  ? PUYO_EMPTY  : left->getPuyoState();
176 
177     int currentIndex = PuyoGroupImageIndex
178 		[downState == currentPuyoState  ? 1 : 0]
179 		[rightState == currentPuyoState ? 1 : 0]
180 		[upState == currentPuyoState    ? 1 : 0]
181 		[leftState == currentPuyoState  ? 1 : 0];
182     switch (currentPuyoState) {
183 		case PUYO_VIOLET:
184 			return puyoFaces[0][currentIndex];
185 		case PUYO_RED:
186 			return puyoFaces[1][currentIndex];
187 		case PUYO_BLUE:
188 			return puyoFaces[2][currentIndex];
189 		case PUYO_GREEN:
190 			return puyoFaces[3][currentIndex];
191 		case PUYO_YELLOW:
192 			return puyoFaces[4][currentIndex];
193 		default:
194 			return getSurfaceForState(currentPuyoState);
195     }
196 }
197 
cycleAnimation()198 void PuyoView::cycleAnimation()
199 {
200     // Cycling every puyo's animation
201 	for (int i = 0, j = attachedGame->getPuyoCount() ; i < j ; i++) {
202 		AnimatedPuyo *currentPuyo =
203         (AnimatedPuyo *)(attachedGame->getPuyoAtIndex(i));
204 		currentPuyo->cycleAnimation();
205 	}
206     // Cycling dead puyo's animations
207     attachedPuyoFactory.cycleWalhalla();
208 
209     // Cycling view's animations
210     if (viewAnimations.getSize() > 0) {
211         PuyoAnimation *currentAnimation = (PuyoAnimation *)(viewAnimations.getElementAt(0));
212         if (currentAnimation->isFinished()) {
213             viewAnimations.removeElementAt(0);
214             delete currentAnimation;
215         }
216         else {
217             currentAnimation->cycle();
218         }
219     }
220 
221     // If there is a skipped cycle to do, do it
222     if ((skippedCycle || attachedGame->isEndOfCycle()) && attachedGame->isGameRunning() && cycleAllowed()) {
223         attachedGame->cycle();
224         skippedCycle = false;
225     }
226 }
227 
cycleGame()228 void PuyoView::cycleGame()
229 {
230     // If we are not allowed to cycle the game, mark it
231     if (cycleAllowed()) {
232         skippedCycle = false;
233         attachedGame->cycle();
234     }
235     else {
236         skippedCycle = true;
237     }
238 }
239 
moveLeft()240 void PuyoView::moveLeft()
241 {
242     if (cycleAllowed()) attachedGame->moveLeft();
243 }
244 
moveRight()245 void PuyoView::moveRight()
246 {
247     if (cycleAllowed()) attachedGame->moveRight();
248 }
249 
rotateLeft()250 void PuyoView::rotateLeft()
251 {
252     if (cycleAllowed()) attachedGame->rotate(true);
253 }
254 
rotateRight()255 void PuyoView::rotateRight()
256 {
257     if (cycleAllowed()) attachedGame->rotate(false);
258 }
259 
260 
render()261 void PuyoView::render()
262 {
263 
264 	SDL_Rect drect;
265     SDL_Rect vrect;
266 	vrect.x = xOffset;
267 	vrect.y = yOffset;
268 	vrect.w = TSIZE * PUYODIMX;
269 	vrect.h = TSIZE * PUYODIMY;
270 
271         bool displayFallings = this->cycleAllowed();
272 
273     for (int i = 0 ; i < PUYODIMX ; i++) {
274         for (int j = 0 ; j < PUYODIMY ; j++) {
275             AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(attachedGame->getPuyoAt(i, j));
276             if ((currentPuyo != NULL) && (displayFallings || !currentPuyo->isFalling()) && (getSurfaceForPuyo(currentPuyo) != neutral)
277                 && (currentPuyo->getVisible()) && (currentPuyo->isRenderingAnimation() == false)) {
278                 drect.x = xOffset + i * TSIZE;
279                 drect.y = yOffset + j * TSIZE;
280                 if (currentPuyo->getPuyoState() < PUYO_EMPTY)
281                     drect.y -= attachedGame->getSemiMove() * TSIZE / 2;
282                 drect.w = puyoShadow->w;
283                 drect.h = puyoShadow->h;
284                 if (drect.y + drect.h > vrect.y + vrect.h)
285 					drect.h -= (drect.y + drect.h - vrect.y - vrect.h);
286                 if (drect.x + drect.w > vrect.x + vrect.w)
287 					drect.w -= (drect.x + drect.w - vrect.x - vrect.w);
288                 attachedPainter.requestDraw(puyoShadow, &drect);
289             }
290         }
291     }
292  	for (int i = 0, j = attachedGame->getPuyoCount() ; i < j ; i++) {
293         AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(attachedGame->getPuyoAtIndex(i));
294         if (displayFallings || !currentPuyo->isFalling()) currentPuyo->render();
295     }
296     // drawing the walhalla
297     attachedPuyoFactory.renderWalhalla();
298 
299 	drect.x = nXOffset;
300 	drect.y = nYOffset;
301 	drect.w = TSIZE;
302 	drect.h = TSIZE * 2;
303 	// Drawing next puyos
304 	IIM_Surface *currentSurface = getSurfaceForState(attachedGame->getNextFalling());
305 	if (currentSurface != NULL) {
306 		drect.x = nXOffset;
307 		drect.y = nYOffset + TSIZE;
308 		drect.w = currentSurface->w;
309 		drect.h = currentSurface->h;
310 		attachedPainter.requestDraw(currentSurface, &drect);
311 		if (currentSurface != neutral) attachedPainter.requestDraw(puyoEyes, &drect);
312 	}
313 	currentSurface = getSurfaceForState(attachedGame->getNextCompanion());
314 	if (currentSurface != NULL) {
315 		drect.x = nXOffset;
316 		drect.y = nYOffset;
317 		drect.w = currentSurface->w;
318 		drect.h = currentSurface->h;
319 		attachedPainter.requestDraw(currentSurface, &drect);
320 		if (currentSurface != neutral) attachedPainter.requestDraw(puyoEyes, &drect);
321 	}
322 
323     // Drawing the view animation
324     if (viewAnimations.getSize() > 0) {
325         PuyoAnimation *currentAnimation = (PuyoAnimation *)(viewAnimations.getElementAt(0));
326         if (!currentAnimation->isFinished()) {
327             currentAnimation->draw(0);
328         }
329     }
330 }
331 
renderNeutral()332 void PuyoView::renderNeutral()
333 {
334 	SDL_Rect drect;
335 
336 	// Drawing neutral puyos
337 	int numBigNeutral = attachedGame->getNeutralPuyos() / PUYODIMX;
338 	int numNeutral = attachedGame->getNeutralPuyos() % PUYODIMX;
339 	int drect_x = xOffset;
340 	int compressor = 0;
341 
342 	int width = numBigNeutral * bigNeutral->w + numNeutral * neutral->w;
343 	if (width > TSIZE * PUYODIMX) {
344 		compressor = (width - TSIZE * PUYODIMX) / (numNeutral + numBigNeutral);
345 	}
346 
347 	for (int cpt = 0 ; cpt < numBigNeutral ; cpt++) {
348 		drect.x = drect_x;
349 		drect.y = yOffset + 3 + TSIZE;
350 		drect.w = bigNeutral->w;
351 		drect.h = bigNeutral->h;
352 		attachedPainter.requestDraw(bigNeutral, &drect);
353 		drect_x += bigNeutral->w - compressor;
354 	}
355 	for (int cpt = 0 ; cpt < numNeutral ; cpt++) {
356 		drect.x = drect_x;
357 		drect.y = yOffset + 3 + TSIZE;
358 		drect.w = neutral->w;
359 		drect.h = neutral->h;
360 		attachedPainter.requestDraw(neutral, &drect);
361 		drect_x += neutral->w - compressor;
362 	}
363 }
364 
gameDidAddNeutral(PuyoPuyo * neutralPuyo,int neutralIndex)365 void PuyoView::gameDidAddNeutral(PuyoPuyo *neutralPuyo, int neutralIndex) {
366     int x = neutralPuyo->getPuyoX();
367     int y = neutralPuyo->getPuyoY();
368     ((AnimatedPuyo *)neutralPuyo)->addAnimation(new NeutralAnimation(*((AnimatedPuyo *)neutralPuyo), neutralIndex * 4));
369 }
370 
companionDidTurn(PuyoPuyo * companionPuyo,int companionVector,bool counterclockwise)371 void PuyoView::companionDidTurn(PuyoPuyo *companionPuyo, int companionVector, bool counterclockwise)
372 {
373     ((AnimatedPuyo *)companionPuyo)->addAnimation(new TurningAnimation(*(AnimatedPuyo *)companionPuyo,
374                                                                        companionVector, counterclockwise));
375 }
376 
puyoDidFall(PuyoPuyo * puyo,int originX,int originY)377 void PuyoView::puyoDidFall(PuyoPuyo *puyo, int originX, int originY)
378 {
379     ((AnimatedPuyo *)puyo)->addAnimation(new FallingAnimation(*(AnimatedPuyo *)puyo, originY, xOffset, yOffset, 16));
380 }
381 
puyoWillVanish(IosVector & puyoGroup,int groupNum,int phase)382 void PuyoView::puyoWillVanish(IosVector &puyoGroup, int groupNum, int phase)
383 {
384     AnimationSynchronizer *synchronizer = new AnimationSynchronizer();
385     viewAnimations.addElement(new VanishSoundAnimation(phase, synchronizer));
386     for (int i = 0, j = puyoGroup.getSize() ; i < j ; i++) {
387         AnimatedPuyo *currentPuyo = (AnimatedPuyo *)(puyoGroup.getElementAt(i));
388         currentPuyo->addAnimation(new VanishAnimation(*currentPuyo, i*2 , xOffset, yOffset, synchronizer));
389     }
390     // A revoir
391     if (groupNum == 0) {
392         if (phase>=2) {
393             audio_sound_play(sound_yahoohoo3[(int)((float)NB_YAHOOHOO3 * random()/(RAND_MAX+1.0))]);
394         }
395         if (phase==1) {
396             audio_sound_play(sound_yahoohoo2[(int)((float)NB_YAHOOHOO2 * random()/(RAND_MAX+1.0))]);
397         }
398         else {
399             audio_sound_play(sound_yahoohoo1[(int)((float)NB_YAHOOHOO1 * random()/(RAND_MAX+1.0))]);
400         }
401     }
402 }
403 
gameDidEndCycle()404 void PuyoView::gameDidEndCycle()
405 {
406 	if (enemyGame != NULL) {
407 		if (attachedGame->getNeutralPuyos() < 0)
408 			enemyGame->increaseNeutralPuyos(- attachedGame->getNeutralPuyos());
409 	}
410 }
411 
cycleAllowed()412 bool PuyoView::cycleAllowed()
413 {
414     if (cycleAllowance < 0)
415         return false;
416     return true;
417 }
418 
419 
gameLost()420 void PuyoView::gameLost()
421 {
422 	gameRunning = false;
423 }
424 
425 
426 
427 
428