LCOV - code coverage report
Current view: top level - Core - OOTrumble.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 9 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          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             : }

Generated by: LCOV version 1.14