1 // Description:
2 //   Boss1 enemy controller.
3 //
4 // Copyright (C) 2005 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 <Boss1Enemy.hpp>
17 
18 #include <Point.hpp>
19 #include <GameState.hpp>
20 #include <Constants.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 #include "GLee.h"
32 
Boss1Enemy(LEnemy * le)33 Boss1Enemy::Boss1Enemy( LEnemy *le):
34     BaseEnemy(le, "Boss1Enemy")
35 {
36     XTRACE();
37 
38     ModelManager &mm = *ModelManagerS::instance();
39     string fullModelName = "models/" + _lEnemy->modelName;
40     _model[0] = mm.getModel( fullModelName + "_Leg1");
41     _model[1] = mm.getModel( fullModelName + "_Leg2");
42     _model[2] = mm.getModel( fullModelName + "_Eye1");
43     _model[3] = mm.getModel( fullModelName + "_Eye2");
44     _model[4] = mm.getModel( fullModelName + "_Face");
45     _model[5] = mm.getModel( fullModelName + "_Teeth");
46 
47     for( int i=0; i<6; i++)
48     {
49 	_offsets[i] = _model[i]->getOffset();
50     }
51 
52     norm(_dir);
53     _energy = 10000;
54     _enemyGroup->newParticle( this, _offset.x, _offset.y, 0);
55 }
56 
~Boss1Enemy()57 Boss1Enemy::~Boss1Enemy()
58 {
59     XTRACE();
60 }
61 
getRadiiCount(void)62 int Boss1Enemy::getRadiiCount(void)
63 {
64     return 6;
65 }
66 
getRadius(int radIndex)67 float Boss1Enemy::getRadius(int radIndex)
68 {
69     return _radii[ radIndex];
70 }
71 
getOffset(int radIndex)72 vec3 Boss1Enemy::getOffset(int radIndex)
73 {
74     return _offsets[ radIndex];
75 }
76 
init(ParticleInfo * p)77 void Boss1Enemy::init( ParticleInfo *p)
78 {
79 //    XTRACE();
80     p->position.z = -100.0;
81     p->extra.x = 0;
82     p->extra.y = 0;
83 
84     _lastLeft = 0;
85     _lastRight = 0;
86 
87     p->damage = 1000;
88     vec3 minpt, maxpt;
89     p->radius = 0.0f;
90     for( int i=0; i<6; i++)
91     {
92 	_model[i]->getBoundingBox( minpt, maxpt);
93 	_radii[i] = (maxpt.x-minpt.x)/1.8; //2.0; make badies a little easied to hit
94     }
95     p->tod = -1;
96 
97     updatePrevs(p);
98 }
99 
update(ParticleInfo * p)100 bool Boss1Enemy::update( ParticleInfo *p)
101 {
102 //    XTRACE();
103     if( p->tod == 0)
104     {
105 	GameState::numObjects--;
106 	if( (_moveState==eAttack) || (_moveState==eRetreat))
107 	{
108 	    GameState::enemyAttackCount--;
109 	}
110 	delete this;
111 	return false;
112     }
113 
114     float dt = GameState::startOfGameStep - _pathStartTime;
115     if( dt < 0)
116     {
117 	return true;
118     }
119     _enableDraw = true;
120 
121     float len = _activePath->length();
122 
123     _prevDir = _dir;
124     if( dt > len)
125     {
126 //LOG_INFO << "dt = " << dt << ", len = " << len << endl;
127 	getNextPath();
128 	dt -= len;
129 	_pathStartTime += len;
130 	_activePath->getTangent( dt, _prevDir);
131     }
132 
133     _activePath->getPos( dt, _pos);
134     _activePath->getTangent( dt, _dir);
135     norm(_dir);
136 
137 ///LOG_INFO << "dt = " << dt << ", len = " << len << endl;
138 ///LOG_INFO << "-p1 = " << _pos.x << "," << _pos.y << endl;
139 ///LOG_INFO << "-off = " << _offset.x << "," << _offset.y << endl;
140 
141     updatePrevs(p);
142 
143     p->extra.y += 5.0f*GAME_STEP_SCALE;
144     p->position.x = _pos.x + _offset.x;
145     p->position.y = _pos.y + _offset.y;
146 //	p->position.z = _pos.z + _offset.z;
147 
148     if( _extraUpdateAfterTransport)
149     {
150 	//We transport the enemy to a staging area. We need to update again
151 	//to make sure we use the new position in the interpolation step
152 	updatePrevs(p);
153 	_extraUpdateAfterTransport = false;
154     }
155 
156     static ParticleGroup *ebg=
157 	ParticleGroupManagerS::instance()->getParticleGroup(
158 	    ENEMIES_BULLETS_GROUP);
159 
160     if( (Random::random() & 0xff) < Skill::getFireProbability())
161     {
162 	ParticleInfo pi;
163 	int whichLeg = (Random::random()&8)>>3;
164 	pi.position = p->position + _offsets[whichLeg];
165 	if( whichLeg == 1)
166 	{
167 	    pi.position = pi.position + vec3(5,-5,0);
168 	    pi.velocity.x =  5.0f*0.2f*GAME_STEP_SCALE;
169 	    pi.velocity.y = -5.0f*0.2f*GAME_STEP_SCALE;
170 	    pi.velocity.z =  0.0f*0.2f*GAME_STEP_SCALE;
171 	    _lastLeft = ebg->newParticle( "SwarmLeader",pi);
172 	}
173 	else
174 	{
175 	    pi.position = pi.position + vec3(-5,-5,0);
176 	    pi.velocity.x = -5.0f*0.2f*GAME_STEP_SCALE;
177 	    pi.velocity.y = -5.0f*0.2f*GAME_STEP_SCALE;
178 	    pi.velocity.z =  0.0f*0.2f*GAME_STEP_SCALE;
179 	    _lastRight = ebg->newParticle( "SwarmLeader",pi);
180 	}
181 	AudioS::instance()->playSample( "sounds/puididl.wav");
182     }
183 
184     if( (Random::random() & 0x1f) < Skill::getFireProbability())
185     {
186 	ParticleInfo pi;
187 
188 	if( _lastLeft)
189 	{
190 	    pi.related = _lastLeft;
191 
192 	    vec3 off2(1,1,0);
193 	    off2 = off2 * ((Random::rangef0_1()-0.5)*5);
194 	    pi.position = p->position + _offsets[1] + vec3(5,-5,0) + off2;
195 	    pi.velocity = vec3( 0.7, -0.7, 0);
196 
197 	    ebg->newParticle( "SwarmElement",pi);
198 	}
199 
200 	if( _lastRight)
201 	{
202 	    pi.related = _lastRight;
203 
204 
205 	    vec3 off2(-1,1,0);
206 	    off2 = off2 * ((Random::rangef0_1()-0.5)*5);
207 	    pi.position = p->position + _offsets[0] + vec3(-5,-5,0) + off2;
208 	    pi.velocity = vec3( -0.7, -0.7, 0);
209 
210 	    ebg->newParticle( "SwarmElement",pi);
211 	}
212     }
213 
214     return true;
215 }
216 
draw(ParticleInfo * p)217 void Boss1Enemy::draw( ParticleInfo *p)
218 {
219 //    XTRACE();
220     if( _enableDraw)
221     {
222 	ParticleInfo pi;
223 	interpolate( p, pi);
224 
225 	glPushMatrix();
226 
227 	glTranslatef( pi.position.x, pi.position.y, pi.position.z);
228 #if 0
229 	if( _moveState != eIdle)
230 	{
231     	    float gf = GameState::frameFraction;
232 	    Point3D _dirInterp;
233 	    _dirInterp.x = _prevDir.x + (_dir.x - _prevDir.x) * gf;
234 	    _dirInterp.y = _prevDir.y + (_dir.y - _prevDir.y) * gf;
235 	    _dirInterp.z = _prevDir.z + (_dir.z - _prevDir.z) * gf;
236 
237 	    alignWith( _dirInterp);
238 	}
239 #endif
240 	glColor4f(1.0,1.0,1.0,1.0);
241 	for( int i=0; i<6; i++)
242 	{
243 	    glPushMatrix();
244 	    glTranslatef( _offsets[i].x, _offsets[i].y, _offsets[i].z);
245 	    _model[i]->draw();
246 	    glPopMatrix();
247 	}
248 
249 	glPopMatrix();
250     }
251 }
252 
hit(ParticleInfo * p,ParticleInfo * p2,int)253 void Boss1Enemy::hit( ParticleInfo *p, ParticleInfo *p2, int /*radIndex*/)
254 {
255 //    XTRACE();
256     static ParticleGroup *effects =
257 	ParticleGroupManagerS::instance()->getParticleGroup( EFFECTS_GROUP2);
258 
259     int scoreAdd = 50;
260     if( p2->damage > 400)
261     {
262 	_energy -= 10;  //workaround for FlankBuster killer
263 	scoreAdd = 10;
264     }
265     else
266 	_energy -= p2->damage;
267 
268     if( (_energy <= 0) && (p->tod != 0))
269     {
270 	static ParticleGroup *bonus =
271 	    ParticleGroupManagerS::instance()->getParticleGroup( BONUS_GROUP);
272 
273 	p->tod = 0;
274 	int newValue = ScoreKeeperS::instance()->addToCurrentScore( 5000);
275 
276 	ParticleInfo pi;
277 	pi.position = p->position;
278 	char buf[10];
279 	sprintf( buf, "%d", newValue);
280 	pi.text = buf;
281 
282 	pi.color.x = 1.0f;
283 	pi.color.y = 1.0f;
284 	pi.color.z = 1.0f;
285 
286 	effects->newParticle( "ScoreHighlight", pi);
287 
288 	bonus->newParticle( "SuperBonus",
289 		p->position.x, p->position.y, p->position.z);
290 
291 	for( int j=0; j<6; j++)
292 	{
293 	    vec3 pos = p->position + _offsets[j];
294 	    //spawn explosion
295 	    for( int i=0; i<(int)(GameState::horsePower/2.0); i++)
296 	    {
297 		effects->newParticle(
298 		    "ExplosionPiece", pos.x, pos.y, pos.z);
299 	    }
300 	}
301 	AudioS::instance()->playSample( "sounds/explosion.wav");
302     }
303     else
304     {
305 	//a hit but no kill
306 	ScoreKeeperS::instance()->addToCurrentScore( scoreAdd);
307 
308 	ParticleInfo pi;
309         pi.position = p2->position;
310 
311 	pi.velocity.x = _dir.x * (0.6f*GAME_STEP_SCALE);
312 	pi.velocity.y = _dir.y * (0.6f*GAME_STEP_SCALE);
313 	pi.velocity.z = 0;
314 
315 	effects->newParticle( "MiniSmoke", pi);
316     }
317 }
318