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