1 // Description:
2 // Basic enemy controller. Controls enemy movement along paths.
3 //
4 // Copyright (C) 2001 Frank Becker
5 //
6 // This program is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free Software
8 // Foundation; either version 2 of the License, or (at your option) any later
9 // version.
10 //
11 // This program is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
14 //
15 #include <Trace.hpp>
16 #include <SimpleEnemy.hpp>
17
18 #include <Point.hpp>
19 #include <Constants.hpp>
20 #include <GameState.hpp>
21 #include <Random.hpp>
22 #include <LevelPack.hpp>
23 #include <ParticleType.hpp>
24 #include <ParticleGroup.hpp>
25 #include <ParticleGroupManager.hpp>
26 #include <StageManager.hpp>
27 #include <Audio.hpp>
28 #include <ScoreKeeper.hpp>
29 #include <ModelManager.hpp>
30
31 //Rolling is currently disabled since it looks totally crappy in some
32 //situations. The idea was for the bugs to lean into the curve (like
33 //a motorcycle for example)..
34 #define WITH_ROLLING 0
35
SimpleEnemy(LEnemy * le)36 SimpleEnemy::SimpleEnemy( LEnemy *le):
37 BaseEnemy(le, "SimpleEnemy")
38 {
39 XTRACE();
40
41 ModelManager &mm = *ModelManagerS::instance();
42 string fullModelName = "models/" + _lEnemy->modelName;
43 _model = mm.getModel( fullModelName);
44
45 #ifdef WITH_ROLLING
46 norm(_dir);
47 #endif
48 _enemyGroup->newParticle( this, _offset.x, _offset.y, 0);
49 }
50
~SimpleEnemy()51 SimpleEnemy::~SimpleEnemy()
52 {
53 XTRACE();
54 }
55
init(ParticleInfo * p)56 void SimpleEnemy::init( ParticleInfo *p)
57 {
58 // XTRACE();
59 p->position.z = -100.0;
60 p->extra.x = 0;
61 p->extra.y = 0;
62
63 p->damage = 100;
64 vec3 minpt, maxpt;
65 _model->getBoundingBox( minpt, maxpt);
66 p->radius = (maxpt.x-minpt.x)/1.8; //2.0; make badies a little easied to hit
67 p->tod = -1;
68
69 updatePrevs(p);
70 }
71
update(ParticleInfo * p)72 bool SimpleEnemy::update( ParticleInfo *p)
73 {
74 // XTRACE();
75 if( p->tod == 0)
76 {
77 GameState::numObjects--;
78 if( (_moveState==eAttack) || (_moveState==eRetreat))
79 {
80 GameState::enemyAttackCount--;
81 }
82 delete this;
83 return false;
84 }
85
86 float dt = GameState::startOfGameStep - _pathStartTime;
87 if( dt < 0)
88 {
89 return true;
90 }
91 _enableDraw = true;
92
93 float len = _activePath->length();
94
95 _prevDir = _dir;
96 if( dt > len)
97 {
98 //LOG_INFO << "dt = " << dt << ", len = " << len << endl;
99 getNextPath();
100 dt -= len;
101 _pathStartTime += len;
102 _activePath->getTangent( dt, _prevDir);
103 }
104 else if( _moveHome || (p->position.y < -57.0))
105 {
106 _moveHome = true;
107 return true;
108 }
109
110 _activePath->getPos( dt, _pos);
111 _activePath->getTangent( dt, _dir);
112
113 #ifdef WITH_ROLLING
114 norm(_dir);
115 if( _moveState != eIdle)
116 {
117 //The area of the triangle for the previous and current direction
118 //vector is an indication of how sharp a turn the enemy is making.
119 //Use this to roll the enemy.
120 Point3D c = cross( _prevDir, _dir);
121 float a = 250*sqrt(dot( c, c));
122 //is enemy turning left or right
123 if( c.z < 0)
124 {
125 a = -a;
126 }
127 p->extra.y = a;
128 // LOG_INFO << "rot = " << p->extra.y << endl;
129 }
130 else
131 {
132 p->extra.y = 0.0;
133 }
134 #endif
135
136 ///LOG_INFO << "dt = " << dt << ", len = " << len << endl;
137 ///LOG_INFO << "-p1 = " << _pos.x << "," << _pos.y << endl;
138 ///LOG_INFO << "-off = " << _offset.x << "," << _offset.y << endl;
139
140 updatePrevs(p);
141
142 p->position.x = _pos.x + _offset.x;
143 p->position.y = _pos.y + _offset.y;
144 // p->position.z = _pos.z + _offset.z;
145
146 if( _extraUpdateAfterTransport)
147 {
148 //We transport the enemy to a staging area. We need to update again
149 //to make sure we use the new position in the interpolation step
150 updatePrevs(p);
151 _extraUpdateAfterTransport = false;
152 }
153
154 static ParticleGroup *ebg=
155 ParticleGroupManagerS::instance()->getParticleGroup(
156 SHOOTABLE_ENEMY_BULLETS_GROUP);
157
158 if( (GameState::enemyBulletCount < Skill::getMaxBullets()) &&
159 ( (_moveState == eIdle) ||
160 (_moveState == eAttack) ||
161 (GameState::skill!=Skill::eRookie)))
162 {
163 p->extra.x += 0.1f*GAME_STEP_SCALE;
164 if( p->extra.x > 0.5)
165 {
166 if( (Random::random() & 0xff) < Skill::getFireProbability())
167 {
168 ebg->newParticle(
169 // "PlasmaBullet",p->position.x,p->position.y,p->position.z);
170 "BallOfFire",p->position.x,p->position.y,p->position.z);
171 AudioS::instance()->playSample( "sounds/laser3.wav");
172 }
173 p->extra.x = 0;
174 }
175 }
176
177 return true;
178 }
179
draw(ParticleInfo * p)180 void SimpleEnemy::draw( ParticleInfo *p)
181 {
182 // XTRACE();
183 if( _enableDraw)
184 {
185 ParticleInfo pi;
186 interpolate( p, pi);
187
188 glPushMatrix();
189
190 glTranslatef( pi.position.x, pi.position.y, pi.position.z);
191 if( _moveState != eIdle)
192 {
193 float gf = GameState::frameFraction;
194 Point3D _dirInterp;
195 _dirInterp.x = _prevDir.x + (_dir.x - _prevDir.x) * gf;
196 _dirInterp.y = _prevDir.y + (_dir.y - _prevDir.y) * gf;
197 _dirInterp.z = _prevDir.z + (_dir.z - _prevDir.z) * gf;
198
199 alignWith( _dirInterp);
200 #if WITH_ROLLING
201 glRotatef( pi.extra.y, 0,1,0);
202 #endif
203 }
204 _model->draw();
205
206 glPopMatrix();
207 }
208 }
209
hit(ParticleInfo * p,int damage,int)210 void SimpleEnemy::hit( ParticleInfo *p, int damage, int /*radIndex*/)
211 {
212 // XTRACE();
213 static ParticleGroup *effects =
214 ParticleGroupManagerS::instance()->getParticleGroup( EFFECTS_GROUP2);
215
216 _energy -= damage;
217 if( _energy <= 0)
218 {
219 static ParticleGroup *bonus =
220 ParticleGroupManagerS::instance()->getParticleGroup( BONUS_GROUP);
221
222 p->tod = 0;
223 if( _moveState == eAttack)
224 {
225 ScoreKeeperS::instance()->addToCurrentScore( 200);
226 }
227 else
228 {
229 ScoreKeeperS::instance()->addToCurrentScore( 100);
230 }
231
232 if( (Random::random() & 0xff) > 10)
233 bonus->newParticle( "Bonus1",
234 p->position.x, p->position.y, p->position.z);
235 else
236 bonus->newParticle( "SuperBonus",
237 p->position.x, p->position.y, p->position.z);
238
239 //spawn explosion
240 for( int i=0; i<(int)(GameState::horsePower/2.0); i++)
241 {
242 effects->newParticle(
243 "ExplosionPiece", p->position.x, p->position.y, p->position.z);
244 }
245 AudioS::instance()->playSample( "sounds/explosion.wav");
246 }
247 else
248 {
249 if( damage > 0)
250 {
251 //a hit but no kill
252 ScoreKeeperS::instance()->addToCurrentScore( 50);
253 }
254
255 ParticleInfo pi;
256
257 pi.position.x = p->position.x;
258 pi.position.y = p->position.y;
259 pi.position.z = p->position.z;
260
261 pi.velocity.x = _dir.x*0.6f*GAME_STEP_SCALE;
262 pi.velocity.y = _dir.y*0.6f*GAME_STEP_SCALE;
263 pi.velocity.z = _dir.z*0.6f*GAME_STEP_SCALE;
264
265 effects->newParticle( "MiniSmoke", pi);
266 }
267 }
268