Line data Source code
1 0 : /*
2 :
3 : OOTrumble.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOTrumble.h"
26 : #import "Universe.h"
27 : #import "PlayerEntity.h"
28 : #import "OOTexture.h"
29 : #import "ResourceManager.h"
30 : #import "OOSound.h"
31 : #import "OOStringParsing.h"
32 : #import "OOMaths.h"
33 : #import "MyOpenGLView.h"
34 :
35 :
36 : static void InitTrumbleSounds(void);
37 : static void PlayTrumbleIdle(void);
38 : static void PlayTrumbleSqueal(void);
39 :
40 :
41 : @implementation OOTrumble
42 :
43 0 : - (id) init
44 : {
45 : self = [super init];
46 :
47 : int i;
48 : for (i = 0; i < 4; i++)
49 : {
50 : colorPoint1[i] = 1.0;
51 : colorPoint2[i] = 1.0;
52 : }
53 :
54 : return self;
55 : }
56 :
57 : - (id) initForPlayer:(PlayerEntity*) p1
58 : {
59 : self = [super init];
60 :
61 : [self setupForPlayer: p1 digram: @"a1"];
62 :
63 : return self;
64 : }
65 :
66 : - (id) initForPlayer:(PlayerEntity*) p1 digram:(NSString*) digramString
67 : {
68 : self = [super init];
69 :
70 : [self setupForPlayer: p1 digram: digramString];
71 :
72 : return self;
73 : }
74 :
75 : - (void) setupForPlayer:(PlayerEntity*) p1 digram:(NSString*) digramString
76 : {
77 : // set digram
78 : //
79 : digram[0] = [digramString characterAtIndex:0];
80 : digram[1] = [digramString characterAtIndex:1];
81 :
82 : // set player
83 : //
84 : player = p1;
85 :
86 : // set color points
87 : int r0 = (int)digram[0];
88 : int r1 = (int)digram[1];
89 : int pointscheme[6] = { 1, 1, 0, 0, 1, 1};
90 : int ps = r0 >> 2; // first digram determines pattern of points
91 : pointscheme[0] = (ps >> 3) & 1;
92 : pointscheme[1] = (ps >> 2) & 1;
93 : pointscheme[2] = (ps >> 1) & 1;
94 : pointscheme[3] = ps & 1;
95 : pointscheme[4] = (ps >> 2) & 1;
96 : pointscheme[5] = (ps >> 3) & 1;
97 :
98 : GLfloat c1[4] = { 1.0, 1.0, 1.0, 1.0};
99 : GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0};
100 :
101 : // I am missing something. Please clarify the intent of the following statement.
102 : // The next statement shift the result of adding two masked values
103 : // max_size = 0.90 + 0.50 * (((r0 & 0x38) + (r1 & 0x38)) >> 3) / 63.0; // inheritable
104 : // The next statement adds masked(r0) to shifted(masked(r1)
105 : // max_size = 0.90 + 0.50 * ((r0 & 0x38) + ((r1 & 0x38) >> 3)) / 63.0; // inheritable
106 : // Sorry, but I cannot determine what you intended to do here.
107 : //
108 : // GILES: It's the second one, we're just determining a pseudo random max_size from the first digram
109 : max_size = 0.90 + 0.50 * ((r0 & 0x38) + ((r1 & 0x38) >> 3)) / 63.0; // inheritable
110 :
111 : // seed the random number generator
112 : //
113 : ranrot_srand(r0 + r1 * 256);
114 :
115 : // set random colors
116 : int col1 = r0 & 7;
117 : int col2 = r1 & 7;
118 : while (((col1 == 7)||(col1 == 0)) && ((col2 == 7)||(col2 == 0))) // monochrome not allowed
119 : {
120 : if (col1 == col2)
121 : col1 = ranrot_rand() & 7;
122 : else
123 : col2 = ranrot_rand() & 7;
124 : }
125 : c1[0] = (GLfloat)(col1 & 1);
126 : c1[1] = (GLfloat)((col1 >> 1) & 1);
127 : c1[2] = (GLfloat)((col1 >> 2) & 1);
128 : c2[0] = (GLfloat)(col2 & 1);
129 : c2[1] = (GLfloat)((col2 >> 1) & 1);
130 : c2[2] = (GLfloat)((col2 >> 2) & 1);
131 : if (col1 == 0)
132 : {
133 : c1[0] = 0.5 + 0.1 * c2[1]; c1[1] = 0.5 + 0.1 * c2[2]; c1[2] = 0.5;
134 : }
135 : if (col1 == 7)
136 : {
137 : c1[0] = 1.0 - 0.1 * c2[1]; c1[1] = 1.0 - 0.1 * c2[2]; c1[2] = 0.9;
138 : }
139 : if (col2 == 0)
140 : {
141 : c2[0] = 0.5 + 0.1 * c1[2]; c2[1] = 0.5 + 0.1 * c1[0]; c2[2] = 0.5;
142 : }
143 : if (col2 == 7)
144 : {
145 : c2[0] = 1.0 - 0.1 * c1[2]; c2[1] = 1.0 - 0.1 * c1[0]; c2[2] = 0.9;
146 : }
147 :
148 : // position and motion
149 : //
150 : position.x = (ranrot_rand() & 15)* 28 - 210;
151 : position.y = (ranrot_rand() & 15)* 28 - 210;
152 : //
153 : [self randomizeMotionX];
154 : [self randomizeMotionY];
155 :
156 : // rotation
157 : //
158 : rotation = TRUMBLE_MAX_ROTATION * (randf() - randf());
159 : rotational_velocity = TRUMBLE_MAX_ROTATIONAL_VELOCITY * (randf() - randf());
160 :
161 : //
162 : int i;
163 : for (i = 0; i < 4; i++)
164 : {
165 : colorPoint1[i] = c1[i];
166 : colorPoint2[i] = c2[i];
167 : }
168 : //
169 : for (i = 0; i < 6; i++)
170 : {
171 : pointColor[i] = colorPoint1;
172 : if (pointscheme[i] == 0)
173 : pointColor[i] = colorPoint1;
174 : if (pointscheme[i] == 1)
175 : pointColor[i] = colorPoint2;
176 : }
177 : //
178 : for (i = 0; i < 4; i++)
179 : {
180 : colorEyes[i] = 0.2 * (2.0 * pointColor[3][i] + 2.0 * pointColor[1][i] + 1.0); // eyes - paler than average
181 : colorBase[i] = 0.5 * (pointColor[2][i] + pointColor[3][i]); // mouth
182 : }
183 : //
184 : size = 0.5 * (1.0 + randf());
185 : [self calcGrowthRate];
186 : hunger = 0.0;
187 : discomfort = 0.0;
188 : //
189 : eye_position = NSMakePoint( 0.0, 0.075 * (randf() - randf()));
190 : eyeFrame = TRUMBLE_EYES_OPEN;
191 : //
192 : mouth_position = NSMakePoint( 0.0, 0.035 * (randf() - randf()));
193 : mouthFrame = TRUMBLE_MOUTH_NORMAL;
194 : //
195 : animation = TRUMBLE_ANIM_IDLE;
196 : nextAnimation = TRUMBLE_ANIM_IDLE;
197 : animationTime = 0.0;
198 : animationDuration = 1.5 + randf() * 3.0; // time until next animation
199 : //
200 : texture = [OOTexture textureWithName:@"trumblekit.png"
201 : inFolder:@"Textures"
202 : options:kOOTextureDefaultOptions | kOOTextureNoShrink
203 : anisotropy:0.0f
204 : lodBias:kOOTextureDefaultLODBias];
205 : [texture retain];
206 :
207 : InitTrumbleSounds();
208 :
209 : readyToSpawn = NO;
210 : }
211 :
212 0 : - (void) dealloc
213 : {
214 : [texture release];
215 :
216 : [super dealloc];
217 : }
218 :
219 : - (void) spawnFrom:(OOTrumble*) parentTrumble
220 : {
221 : if (parentTrumble)
222 : {
223 : // mutate..
224 : unichar mutation1 = ranrot_rand() & ranrot_rand() & ranrot_rand() & 0xff; // each bit has a 1/8 chance of being set
225 : unichar mutation2 = ranrot_rand() & ranrot_rand() & ranrot_rand() & 0xff; // each bit has a 1/8 chance of being set
226 : unichar* parentdigram = [parentTrumble digram];
227 : unichar newdigram[2];
228 : newdigram[0] = parentdigram[0] ^ mutation1;
229 : newdigram[1] = parentdigram[1] ^ mutation2;
230 : //
231 : [self setupForPlayer: player digram: [NSString stringWithCharacters:newdigram length:2]];
232 : //
233 : size = [parentTrumble size] * 0.4;
234 : if (size < 0.5)
235 : size = 0.5; // minimum size
236 : position = [parentTrumble position];
237 : rotation = [parentTrumble rotation];
238 : movement = [parentTrumble movement];
239 : movement.y += 8.0; // emerge!
240 : }
241 : else
242 : {
243 : size = 0.5; // minimum size
244 : position.x = (ranrot_rand() & 15)* 28 - 210;
245 : position.y = (ranrot_rand() & 15)* 28 - 210;
246 : [self randomizeMotionX];
247 : [self randomizeMotionY];
248 : rotation = TRUMBLE_MAX_ROTATION * (randf() - randf());
249 : rotational_velocity = TRUMBLE_MAX_ROTATIONAL_VELOCITY * (randf() - randf());
250 : }
251 : hunger = 0.25;
252 : [self calcGrowthRate];
253 : discomfort = 0.0;
254 : [self actionSleep];
255 : }
256 :
257 : - (void) calcGrowthRate
258 : {
259 : float rsize = size / max_size;
260 : growth_rate = TRUMBLE_GROWTH_RATE * (1.0 - rsize);
261 : }
262 :
263 :
264 : - (unichar *) digram
265 : {
266 : return digram;
267 : }
268 :
269 : - (NSPoint) position
270 : {
271 : return position;
272 : }
273 :
274 : - (NSPoint) movement
275 : {
276 : return movement;
277 : }
278 :
279 : - (GLfloat) rotation
280 : {
281 : return rotation;
282 : }
283 :
284 : - (GLfloat) size
285 : {
286 : return size;
287 : }
288 :
289 : - (GLfloat) hunger
290 : {
291 : return hunger;
292 : }
293 :
294 : - (GLfloat) discomfort
295 : {
296 : return discomfort;
297 : }
298 :
299 :
300 :
301 : // AI methods here
302 : - (void) actionIdle
303 : {
304 : nextAnimation = TRUMBLE_ANIM_IDLE;
305 : animationDuration = 1.5 + 3.0 * randf(); // time until next animation
306 : }
307 :
308 : - (void) actionBlink
309 : {
310 : nextAnimation = TRUMBLE_ANIM_BLINK;
311 : animationDuration = 0.5 + 0.5 * randf(); // time until next animation
312 : }
313 :
314 : - (void) actionSnarl
315 : {
316 : nextAnimation = TRUMBLE_ANIM_SNARL;
317 : animationDuration = 4.0 + 1.0 * randf(); // time until next animation
318 : }
319 :
320 : - (void) actionProot
321 : {
322 : nextAnimation = TRUMBLE_ANIM_PROOT;
323 : animationDuration = 1.5 + 0.5 * randf(); // time until next animation
324 : }
325 :
326 : - (void) actionShudder
327 : {
328 : nextAnimation = TRUMBLE_ANIM_SHUDDER;
329 : animationDuration = 2.25 + randf() * 1.5; // time until next animation
330 : }
331 :
332 : - (void) actionStoned
333 : {
334 : nextAnimation = TRUMBLE_ANIM_STONED;
335 : animationDuration = 1.5 + randf() * 3.0; // time until next animation
336 : }
337 :
338 : - (void) actionPop
339 : {
340 : nextAnimation = TRUMBLE_ANIM_DIE;
341 : animationDuration = 1.5 + randf() * 3.0; // time until next animation
342 : }
343 :
344 : - (void) actionSleep
345 : {
346 : nextAnimation = TRUMBLE_ANIM_SLEEP;
347 : animationDuration = 12.0 + 12.0 * randf(); // time until next animation
348 : }
349 :
350 : - (void) actionSpawn
351 : {
352 : nextAnimation = TRUMBLE_ANIM_SPAWN;
353 : animationDuration = 9.0 + 3.0 * randf(); // time until next animation
354 : }
355 :
356 :
357 : - (void) randomizeMotionX
358 : {
359 : movement.x = 36 * (randf() - 0.5);
360 : movement.x += (movement.x > 0)? 2.0: -2.0;
361 : rotational_velocity = TRUMBLE_MAX_ROTATIONAL_VELOCITY * (randf() - randf());
362 : }
363 :
364 : - (void) randomizeMotionY
365 : {
366 : movement.y = 36 * (randf() - 0.5);
367 : movement.y += (movement.y > 0)? 2.0: -2.0;
368 : rotational_velocity = TRUMBLE_MAX_ROTATIONAL_VELOCITY * (randf() - randf());
369 : }
370 :
371 : - (void) drawTrumble:(double) z
372 : {
373 : /*
374 : draws a trumble body as a fan of triangles...
375 : 2-------3-------4
376 : | \ | / |
377 : | \ | / |
378 : | \ | / |
379 : 1-------0-------5
380 :
381 : */
382 : GLfloat wd = 96 * size;
383 : GLfloat ht = 96 * size;
384 : OOGL(glShadeModel(GL_SMOOTH));
385 : OOGL(glEnable(GL_TEXTURE_2D));
386 :
387 : OOGLPushModelView();
388 :
389 : OOGLTranslateModelView(make_vector(position.x, position.y, z));
390 : OOGLMultModelView(OOMatrixForRotationZ(rotation));
391 :
392 : [texture apply];
393 :
394 : //
395 : // Body..
396 : //
397 : OOGLBEGIN(GL_TRIANGLE_FAN);
398 : glColor4fv(pointColor[3]);
399 : glTexCoord2f( 0.25, 0.5);
400 : glVertex2f( 0.0, -0.5 * ht);
401 :
402 : glColor4fv(pointColor[0]);
403 : glTexCoord2f( 0.0, 0.5);
404 : glVertex2f( -0.5 * wd, -0.5 * ht);
405 :
406 : glColor4fv(pointColor[1]);
407 : glTexCoord2f( 0.0, 0.0);
408 : glVertex2f( -0.5 * wd, 0.5 * ht);
409 :
410 : glColor4fv(pointColor[2]);
411 : glTexCoord2f( 0.25, 0.0);
412 : glVertex2f( 0.0, 0.5 * ht);
413 :
414 : glColor4fv(pointColor[4]);
415 : glTexCoord2f( 0.5, 0.0);
416 : glVertex2f( 0.5 * wd, 0.5 * ht);
417 :
418 : glColor4fv(pointColor[5]);
419 : glTexCoord2f( 0.5, 0.5);
420 : glVertex2f( 0.5 * wd, -0.5 * ht);
421 : OOGLEND();
422 :
423 : //
424 : // Eyes
425 : //
426 : GLfloat eyeTextureOffset = 0.0;
427 : switch(eyeFrame)
428 : {
429 : case TRUMBLE_EYES_NONE :
430 : case TRUMBLE_EYES_OPEN :
431 : eyeTextureOffset = 0.0; break;
432 : case TRUMBLE_EYES_SHUT :
433 : eyeTextureOffset = 0.25; break;
434 : case TRUMBLE_EYES_WIDE :
435 : eyeTextureOffset = 0.5; break;
436 : }
437 :
438 : OOGLTranslateModelView(make_vector(eye_position.x * wd, eye_position.y * ht, 0.0));
439 :
440 : OOGL(glColor4fv(colorEyes));
441 : OOGLBEGIN(GL_QUADS);
442 : glTexCoord2f( 0.5, eyeTextureOffset);
443 : glVertex2f( -0.5 * wd, 0.20 * ht);
444 :
445 : glTexCoord2f( 1.0, eyeTextureOffset);
446 : glVertex2f( 0.5 * wd, 0.20 * ht);
447 :
448 : glTexCoord2f( 1.0, eyeTextureOffset + 0.25);
449 : glVertex2f( 0.5 * wd, -0.30 * ht);
450 :
451 : glTexCoord2f( 0.5, eyeTextureOffset + 0.25);
452 : glVertex2f( -0.5 * wd, -0.30 * ht);
453 : OOGLEND();
454 :
455 : //
456 : // Mouth
457 : //
458 : GLfloat mouthTextureOffset = 0.0;
459 : switch(mouthFrame)
460 : {
461 : case TRUMBLE_MOUTH_POUT :
462 : mouthTextureOffset = 0.500; break;
463 : case TRUMBLE_MOUTH_NONE :
464 : case TRUMBLE_MOUTH_NORMAL :
465 : mouthTextureOffset = 0.625; break;
466 : case TRUMBLE_MOUTH_GROWL:
467 : mouthTextureOffset = 0.750; break;
468 : case TRUMBLE_MOUTH_SNARL:
469 : mouthTextureOffset = 0.875; break;
470 : }
471 :
472 : OOGLTranslateModelView(make_vector(mouth_position.x * wd, mouth_position.y * ht, 0.0));
473 :
474 : OOGL(glColor4fv(colorBase));
475 : OOGLBEGIN(GL_QUADS);
476 : glTexCoord2f( 0.0, mouthTextureOffset);
477 : glVertex2f( -0.25 * wd, -0.10 * ht);
478 :
479 : glTexCoord2f( 0.25, mouthTextureOffset);
480 : glVertex2f( 0.25 * wd, -0.10 * ht);
481 :
482 : glTexCoord2f( 0.25, mouthTextureOffset + 0.125);
483 : glVertex2f( 0.25 * wd, -0.35 * ht);
484 :
485 : glTexCoord2f( 0.0, mouthTextureOffset + 0.125);
486 : glVertex2f( -0.25 * wd, -0.35 * ht);
487 : OOGLEND();
488 :
489 : // finally..
490 : OOGLPopModelView();
491 : OOGL(glDisable(GL_TEXTURE_2D));
492 : }
493 :
494 : - (void) updateTrumble:(double) delta_t
495 : {
496 : // player movement
497 : NSPoint p_mov = NSMakePoint(TRUMBLE_MAX_ROTATIONAL_VELOCITY * [player dialPitch], TRUMBLE_MAX_ROTATIONAL_VELOCITY * [player dialRoll]);
498 : switch ([UNIVERSE viewDirection])
499 : {
500 : GLfloat t;
501 : case VIEW_AFT:
502 : p_mov.x = -p_mov.x;
503 : p_mov.y = -p_mov.y;
504 : break;
505 : case VIEW_STARBOARD:
506 : t = p_mov.x;
507 : p_mov.x = -p_mov.y;
508 : p_mov.y = t;
509 : break;
510 : case VIEW_PORT:
511 : t = p_mov.x;
512 : p_mov.x = p_mov.y;
513 : p_mov.y = -t;
514 : break;
515 :
516 : default:
517 : break;
518 : }
519 : p_mov.x *= -4.0;
520 :
521 : // movement
522 : //
523 : GLfloat wd = 0.5 * 96 * size;
524 : GLfloat ht = 0.5 * 96 * size;
525 : //
526 : GLfloat bumpx = 320 * 1.5 - wd;
527 : GLfloat bumpy = 240 * 1.5 - ht;
528 : //
529 : position.x += delta_t * movement.x;
530 : if ((position.x < -bumpx)||(position.x > bumpx))
531 : {
532 : position.x = (position.x < -bumpx)? -bumpx : bumpx;
533 : [self randomizeMotionX];
534 : }
535 : position.y += delta_t * (movement.y + p_mov.x);
536 : if ((position.y < -bumpy)||(position.y > bumpy))
537 : {
538 : position.y = (position.y < -bumpy)? -bumpy : bumpy;
539 : [self randomizeMotionY];
540 : }
541 :
542 : // rotation
543 : //
544 : rotation += delta_t * (rotational_velocity + p_mov.y);
545 : if (animation != TRUMBLE_ANIM_DIE)
546 : {
547 : if (rotation < -TRUMBLE_MAX_ROTATION)
548 : {
549 : rotation = -TRUMBLE_MAX_ROTATION;
550 : rotational_velocity = TRUMBLE_MAX_ROTATIONAL_VELOCITY * 0.5 * (randf() + randf());
551 : }
552 : if (rotation > TRUMBLE_MAX_ROTATION)
553 : {
554 : rotation = TRUMBLE_MAX_ROTATION;
555 : rotational_velocity = -TRUMBLE_MAX_ROTATIONAL_VELOCITY * 0.5 * (randf() + randf());
556 : }
557 : }
558 : // growth
559 : //
560 : size += delta_t * growth_rate;
561 : hunger += delta_t * (growth_rate + TRUMBLE_GROWTH_RATE);
562 : if (size > max_size) // fully_grown.. stop growing
563 : {
564 : size = max_size;
565 : growth_rate = 0.0;
566 : }
567 : [self calcGrowthRate];
568 : if (hunger > 0.75)
569 : growth_rate = 0.0;
570 : if (hunger > 1.0)
571 : hunger = 1.0; // clamp
572 :
573 : // feelings
574 : //
575 : GLfloat temp = [player hullHeatLevel];
576 : discomfort += delta_t * hunger * 0.02 * (1.0 - hunger);
577 : if (temp > 0.33)
578 : discomfort += delta_t * (temp - 0.33) * (temp - 0.33) * 0.05;
579 : if (discomfort > 1.0)
580 : discomfort = 1.0; // clamp
581 :
582 : // feeding & reproducing
583 : //
584 : // am I really hungry?
585 : if (hunger > 0.50)
586 : {
587 : // consult menu...
588 : ShipEntity *selectedCargopod = nil;
589 : float mostYummy = 0.0;
590 : NSMutableArray *cargopods = [player cargo]; // the cargo pods
591 : NSUInteger i, n_pods = [cargopods count];
592 : for (i = 0 ; i < n_pods; i++)
593 : {
594 : ShipEntity *cargopod = [cargopods objectAtIndex:i];
595 : OOCommodityType cargo_type = [cargopod commodityType];
596 : float yumminess = (1.0 + randf()) * [[UNIVERSE commodityMarket] trumbleOpinionForGood:cargo_type];
597 : if (yumminess > mostYummy)
598 : {
599 : selectedCargopod = cargopod;
600 : mostYummy = yumminess;
601 : }
602 : }
603 : if (selectedCargopod)
604 : {
605 : // feed
606 : float trumbleAppetiteAccumulator = [player trumbleAppetiteAccumulator];
607 :
608 : trumbleAppetiteAccumulator += hunger;
609 : hunger = 0.0;
610 : discomfort -= mostYummy * 0.5;
611 : if (discomfort < 0.0)
612 : discomfort = 0.0;
613 : if (trumbleAppetiteAccumulator > 10.0)
614 : {
615 : // eaten all of this cargo!
616 : NSString* ms = [NSString stringWithFormat:DESC(@"trumbles-eat-@"),
617 : [UNIVERSE displayNameForCommodity:[selectedCargopod commodityType]]];
618 :
619 : [UNIVERSE addMessage: ms forCount: 4.5];
620 : [cargopods removeObject:selectedCargopod];
621 : trumbleAppetiteAccumulator -= 10.0;
622 :
623 : // consider breeding - must be full grown and happy
624 : if ((size > 0.95)&&(discomfort < 0.25))
625 : {
626 : readyToSpawn = YES;
627 : }
628 :
629 : [player setTrumbleAppetiteAccumulator:trumbleAppetiteAccumulator];
630 : }
631 : }
632 : }
633 :
634 : // animations
635 : //
636 : switch (animation)
637 : {
638 : case TRUMBLE_ANIM_SNARL :
639 : [self updateSnarl: delta_t]; break;
640 : case TRUMBLE_ANIM_SHUDDER :
641 : [self updateShudder: delta_t]; break;
642 : case TRUMBLE_ANIM_STONED :
643 : [self updateStoned: delta_t]; break;
644 : case TRUMBLE_ANIM_DIE :
645 : [self updatePop: delta_t]; break;
646 : case TRUMBLE_ANIM_BLINK :
647 : [self updateBlink: delta_t]; break;
648 : case TRUMBLE_ANIM_PROOT :
649 : [self updateProot: delta_t]; break;
650 : case TRUMBLE_ANIM_SLEEP :
651 : [self updateSleep: delta_t]; break;
652 : case TRUMBLE_ANIM_SPAWN :
653 : [self updateSpawn: delta_t]; break;
654 : case TRUMBLE_ANIM_IDLE :
655 : default:
656 : [self updateIdle: delta_t]; break;
657 : }
658 :
659 :
660 : }
661 :
662 : - (void) updateIdle:(double) delta_t
663 : {
664 : animationTime += delta_t;
665 : if (animationTime > animationDuration)
666 : {
667 : // blink or proot or idle and/or change direction
668 : [self actionIdle];
669 : if (randf() < 0.25)
670 : [self actionBlink];
671 : if (randf() < 0.10)
672 : [self randomizeMotionX];
673 : if (randf() < 0.10)
674 : [self randomizeMotionY];
675 : if (randf() < 0.05)
676 : [self actionProot];
677 : if (randf() < 0.01)
678 : [self actionSleep];
679 : if (randf() < 0.01)
680 : [self actionSnarl];
681 : //
682 : if (readyToSpawn)
683 : {
684 : [self actionSpawn];
685 : readyToSpawn = NO;
686 : }
687 : //
688 : if (discomfort > 0.5 + randf())
689 : {
690 : [self actionShudder];
691 : }
692 : //
693 : if (discomfort > 0.96)
694 : {
695 : [self actionPop];
696 : }
697 : //
698 : animation = nextAnimation;
699 : animationTime = 0.0;
700 : }
701 : }
702 :
703 : - (void) updateBlink:(double) delta_t
704 : {
705 : eyeFrame = TRUMBLE_EYES_SHUT;
706 : animationTime += delta_t;
707 : if (animationTime > animationDuration)
708 : {
709 : // blink or proot or idle
710 : [self actionIdle];
711 : if (randf() < 0.05)
712 : [self actionBlink];
713 : if (randf() < 0.1)
714 : [self actionProot];
715 : animation = nextAnimation;
716 : animationTime = 0.0;
717 : eyeFrame = TRUMBLE_EYES_OPEN;
718 : }
719 : }
720 :
721 : - (void) updateSnarl:(double) delta_t
722 : {
723 : int pc = 100 * animationTime / animationDuration;
724 : if (pc < 25)
725 : {
726 : eyeFrame = TRUMBLE_EYES_SHUT;
727 : mouthFrame = TRUMBLE_MOUTH_GROWL;
728 : }
729 : if ((pc >=25)&&(pc < 90))
730 : {
731 : double vibr = (pc & 1)? -1.0 : 1.0;
732 : if (digram[1] & 4)
733 : position.x += size * vibr * 0.5;
734 : else
735 : position.y += size * vibr * 0.5;
736 : eyeFrame = TRUMBLE_EYES_WIDE;
737 : if (pc & 2)
738 : mouthFrame = TRUMBLE_MOUTH_SNARL;
739 : else
740 : mouthFrame = TRUMBLE_MOUTH_GROWL;
741 : }
742 : if ((pc >=90)&&(pc < 100))
743 : {
744 : eyeFrame = TRUMBLE_EYES_WIDE;
745 : mouthFrame = TRUMBLE_MOUTH_GROWL;
746 : }
747 : animationTime += delta_t;
748 : if (animationTime > animationDuration)
749 : {
750 : // blink or idle
751 : [self actionIdle];
752 : if (randf() < 0.1)
753 : [self actionBlink];
754 : animation = nextAnimation;
755 : animationTime = 0.0;
756 : eyeFrame = TRUMBLE_EYES_OPEN;
757 : mouthFrame = TRUMBLE_MOUTH_NORMAL;
758 : }
759 : }
760 :
761 : - (void) updateProot:(double) delta_t
762 : {
763 : if (!animationTime)
764 : {
765 : animationStage = 0;
766 : }
767 : int pc = 100 * animationTime / animationDuration;
768 : if (pc < 10)
769 : {
770 : eyeFrame = TRUMBLE_EYES_SHUT;
771 : mouthFrame = TRUMBLE_MOUTH_POUT;
772 : }
773 : if (pc >=10)
774 : {
775 : double vibr = (pc & 2)? -1.0 : 1.0;
776 : position.x += size * vibr * 0.25;
777 : eyeFrame = TRUMBLE_EYES_SHUT;
778 : mouthFrame = TRUMBLE_MOUTH_GROWL;
779 : if (!animationStage)
780 : {
781 : animationStage = 1;
782 : PlayTrumbleIdle();
783 : }
784 : }
785 : animationTime += delta_t;
786 : if (animationTime > animationDuration)
787 : {
788 : // blink or idle
789 : [self actionIdle];
790 : if (randf() < 0.1)
791 : [self actionBlink];
792 : animation = nextAnimation;
793 : animationTime = 0.0;
794 : eyeFrame = TRUMBLE_EYES_OPEN;
795 : mouthFrame = TRUMBLE_MOUTH_NORMAL;
796 : }
797 : }
798 :
799 : - (void) updateShudder:(double) delta_t
800 : {
801 : if (!animationTime)
802 : {
803 : eyeFrame = TRUMBLE_EYES_WIDE;
804 : mouthFrame = TRUMBLE_MOUTH_GROWL;
805 : PlayTrumbleSqueal();
806 : }
807 : int pc = 100 * animationTime / animationDuration;
808 : if (pc < 10)
809 : {
810 : eyeFrame = TRUMBLE_EYES_WIDE;
811 : mouthFrame = TRUMBLE_MOUTH_GROWL;
812 : }
813 : if (pc >= 10)
814 : {
815 : double vibr = (pc & 2)? -0.25 : 0.25;
816 : position.x += size * vibr;
817 : eyeFrame = TRUMBLE_EYES_OPEN;
818 : mouthFrame = TRUMBLE_MOUTH_GROWL;
819 : }
820 : animationTime += delta_t;
821 : if (animationTime > animationDuration)
822 : {
823 : // feel better
824 : discomfort *= 0.9;
825 : // blink or idle
826 : [self actionIdle];
827 : if (randf() < 0.1)
828 : [self actionBlink];
829 : animation = nextAnimation;
830 : animationTime = 0.0;
831 : eyeFrame = TRUMBLE_EYES_OPEN;
832 : mouthFrame = TRUMBLE_MOUTH_NORMAL;
833 : }
834 : }
835 :
836 : - (void) updateStoned:(double) delta_t
837 : {
838 : }
839 :
840 : - (void) updatePop:(double) delta_t
841 : {
842 : if (!animationTime)
843 : {
844 : eyeFrame = TRUMBLE_EYES_SHUT;
845 : mouthFrame = TRUMBLE_MOUTH_GROWL;
846 : movement.y = (ranrot_rand() & 7);
847 : if (randf() < 0.5)
848 : rotational_velocity = 63 + (ranrot_rand() & 127);
849 : else
850 : rotational_velocity = -63 - (ranrot_rand() & 127);
851 : // squeal here!
852 : PlayTrumbleSqueal();
853 : }
854 : float pc = animationTime / animationDuration;
855 :
856 : // fading alpha
857 : colorPoint1[1] *= (1.0 - delta_t);
858 : colorPoint2[1] *= (1.0 - delta_t);
859 : colorPoint1[2] *= (1.0 - delta_t);
860 : colorPoint2[2] *= (1.0 - delta_t);
861 : colorPoint1[3] = (1.0 - pc);
862 : colorPoint2[3] = (1.0 - pc);
863 : colorBase[3] = (1.0 - pc);
864 :
865 : // falling
866 : movement.y -= delta_t * 98.0;
867 : rotational_velocity *= (1.0 + delta_t);
868 :
869 : // shrinking
870 : size -= delta_t * (1.0 - pc) * size;
871 :
872 : animationTime += delta_t;
873 : if (animationTime > animationDuration)
874 : {
875 : // kaputnik!
876 : [player removeTrumble:self];
877 : }
878 : }
879 :
880 : - (void) updateSleep:(double) delta_t
881 : {
882 : if (!animationTime)
883 : {
884 : saved_float1 = eye_position.y;
885 : saved_float2 = mouth_position.y;
886 : }
887 : eyeFrame = TRUMBLE_EYES_SHUT;
888 : int pc = 512 * animationTime / animationDuration;
889 : if (pc & 16)
890 : {
891 : double vibr = (pc & 2)? -0.0025 : 0.0025;
892 : eye_position.y += size * vibr;
893 : mouth_position.y += size * vibr * -0.5;
894 : }
895 : else
896 : {
897 : eye_position.y = saved_float1;
898 : mouth_position.y = saved_float2;
899 : }
900 : animationTime += delta_t;
901 : if (animationTime > animationDuration)
902 : {
903 : // idle or proot
904 : eye_position.y = saved_float1;
905 : mouth_position.y = saved_float2;
906 : [self actionIdle];
907 : if (randf() < 0.25)
908 : [self actionProot];
909 : animation = nextAnimation;
910 : animationTime = 0.0;
911 : eyeFrame = TRUMBLE_EYES_OPEN;
912 : }
913 : }
914 :
915 : - (void) updateSpawn:(double) delta_t
916 : {
917 : movement.x *= (1.0 - delta_t);
918 : movement.y *= (1.0 - delta_t);
919 : rotation *= (1.0 - delta_t);
920 : rotational_velocity *= (1.0 - delta_t);
921 : eyeFrame = TRUMBLE_EYES_WIDE;
922 : mouthFrame = TRUMBLE_MOUTH_POUT;
923 : int pc = 256 * animationTime / animationDuration;
924 : double vibr = (pc & 2)? -0.002 * pc : 0.002 * pc;
925 : position.x += size * vibr;
926 : animationTime += delta_t;
927 : if (animationTime > animationDuration)
928 : {
929 : // proot
930 : eye_position.y = saved_float1;
931 : mouth_position.y = saved_float2;
932 : [self actionProot];
933 : animation = nextAnimation;
934 : animationTime = 0.0;
935 : eyeFrame = TRUMBLE_EYES_OPEN;
936 : mouthFrame = TRUMBLE_MOUTH_NORMAL;
937 : [self randomizeMotionX];
938 : [player addTrumble:self];
939 : }
940 : }
941 :
942 : - (NSDictionary*) dictionary
943 : {
944 : return [NSDictionary dictionaryWithObjectsAndKeys:
945 : [NSString stringWithCharacters:digram length:2], @"digram",
946 : [NSNumber numberWithFloat:hunger], @"hunger",
947 : [NSNumber numberWithFloat:discomfort], @"discomfort",
948 : [NSNumber numberWithFloat:size], @"size",
949 : [NSNumber numberWithFloat:growth_rate], @"growth_rate",
950 : [NSNumber numberWithFloat:rotation], @"rotation",
951 : [NSNumber numberWithFloat:rotational_velocity], @"rotational_velocity",
952 : StringFromPoint(position), @"position",
953 : StringFromPoint(movement), @"movement",
954 : nil];
955 : }
956 :
957 : - (void) setFromDictionary:(NSDictionary*) dict
958 : {
959 : NSString* digramString = (NSString*)[dict objectForKey:@"digram"];
960 : [self setupForPlayer: player digram: digramString];
961 : hunger = [[dict objectForKey: @"hunger"] floatValue];
962 : discomfort = [[dict objectForKey: @"discomfort"] floatValue];
963 : size = [[dict objectForKey: @"size"] floatValue];
964 : growth_rate = [[dict objectForKey: @"growth_rate"] floatValue];
965 : rotation = [[dict objectForKey: @"rotation"] floatValue];
966 : rotational_velocity = [[dict objectForKey: @"rotational_velocity"] floatValue];
967 : position = PointFromString([dict objectForKey: @"position"]);
968 : movement = PointFromString([dict objectForKey: @"movement"]);
969 : }
970 :
971 : @end
972 :
973 :
974 0 : static OOSoundSource *sTrumbleSoundSource;
975 0 : static OOSound *sTrumbleIdleSound;
976 0 : static OOSound *sTrumbleSqealSound;
977 :
978 0 : static void InitTrumbleSounds(void)
979 : {
980 : if (sTrumbleSoundSource == nil)
981 : {
982 : sTrumbleSoundSource = [[OOSoundSource alloc] init];
983 : sTrumbleIdleSound = [[OOSound alloc] initWithCustomSoundKey:@"[trumble-idle]"];
984 : sTrumbleSqealSound = [[OOSound alloc] initWithCustomSoundKey:@"[trumble-squeal]"];
985 : }
986 : }
987 :
988 :
989 0 : static void PlayTrumbleIdle(void)
990 : {
991 : // Only play idle sound if no trumble is making noise.
992 : if (![sTrumbleSoundSource isPlaying])
993 : {
994 : // trumble sound from random direction - where's it gone now?
995 : [sTrumbleSoundSource setPosition:OORandomUnitVector()];
996 : [sTrumbleSoundSource playSound:sTrumbleIdleSound];
997 : }
998 : }
999 :
1000 :
1001 0 : static void PlayTrumbleSqueal(void)
1002 : {
1003 : // Play squeal sound if no trumble is currently squealing, but trumping idle sound.
1004 : if (![sTrumbleSoundSource isPlaying] || [sTrumbleSoundSource sound] == sTrumbleIdleSound)
1005 : {
1006 : // trumble sound from random direction - where's it gone now?
1007 : [sTrumbleSoundSource setPosition:OORandomUnitVector()];
1008 : [sTrumbleSoundSource playSound:sTrumbleSqealSound];
1009 : }
1010 : }
|