1 /*
2  * This source file is part of libRocket, the HTML/CSS Interface Middleware
3  *
4  * For the latest information, see http://www.librocket.com
5  *
6  * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  *
26  */
27 
28 #include "Invader.h"
29 #include <Rocket/Core/Math.h>
30 #include <Shell.h>
31 #include <ShellOpenGL.h>
32 #include "Defender.h"
33 #include "Game.h"
34 #include "GameDetails.h"
35 #include "Shield.h"
36 #include "Sprite.h"
37 
38 const float BOMB_UPDATE_FREQ = 0.04f;
39 const float BOMB_RAY_SPEED = 10;
40 const float BOMB_MISSILE_SPEED = 7;
41 const float BOMB_PROBABILITY_EASY = 0.002f;
42 const float BOMB_PROBABILITY_HARD = 0.005f;
43 const float EXPLOSION_TIME = 0.25f;
44 const Rocket::Core::Colourb MOTHERSHIP_COLOUR = Rocket::Core::Colourb(255, 0, 0, 255);
45 
46 Sprite invader_sprites[] =
47 {
48 	// Rank 1
49 	Sprite(Rocket::Core::Vector2f(48, 32), Rocket::Core::Vector2f(0.609375f, 0), Rocket::Core::Vector2f(0.796875f, 0.5f)),
50 	Sprite(Rocket::Core::Vector2f(48, 32), Rocket::Core::Vector2f(0.80078125f, 0), Rocket::Core::Vector2f(0.98828125f, 0.5f)),
51 	// Rank 2
52 	Sprite(Rocket::Core::Vector2f(44, 32), Rocket::Core::Vector2f(0.2578125f, 0), Rocket::Core::Vector2f(0.4296875f, 0.5f)),
53 	Sprite(Rocket::Core::Vector2f(44, 32), Rocket::Core::Vector2f(0.43359375f, 0), Rocket::Core::Vector2f(0.60546875f, 0.5f)),
54 	// Rank 3
55 	Sprite(Rocket::Core::Vector2f(32, 32), Rocket::Core::Vector2f(0, 0), Rocket::Core::Vector2f(0.125f, 0.5f)),
56 	Sprite(Rocket::Core::Vector2f(32, 32), Rocket::Core::Vector2f(0.12890625f, 0), Rocket::Core::Vector2f(0.25390625f, 0.5f)),
57 	// Mothership
58 	Sprite(Rocket::Core::Vector2f(64, 28), Rocket::Core::Vector2f(0.23828125f, 0.515625f), Rocket::Core::Vector2f(0.48828125f, 0.953125f)),
59 	// Explosion
60 	Sprite(Rocket::Core::Vector2f(52, 28), Rocket::Core::Vector2f(0.71484375f, 0.51562500f), Rocket::Core::Vector2f(0.91796875f, 0.95312500f))
61 };
62 
63 Sprite bomb_sprites[] =
64 {
65 	// Ray
66 	Sprite(Rocket::Core::Vector2f(12, 20), Rocket::Core::Vector2f(0.51171875f, 0.51562500f), Rocket::Core::Vector2f(0.55859375f, 0.82812500f)),
67 	Sprite(Rocket::Core::Vector2f(12, 20), Rocket::Core::Vector2f(0.56250000, 0.51562500), Rocket::Core::Vector2f(0.60937500, 0.82812500)),
68 	Sprite(Rocket::Core::Vector2f(12, 20), Rocket::Core::Vector2f(0.61328125, 0.51562500), Rocket::Core::Vector2f(0.66015625, 0.82812500)),
69 	Sprite(Rocket::Core::Vector2f(12, 20), Rocket::Core::Vector2f(0.66406250, 0.51562500), Rocket::Core::Vector2f(0.71093750, 0.82812500)),
70 	// Missile
71 	Sprite(Rocket::Core::Vector2f(12, 20), Rocket::Core::Vector2f(0.92578125, 0.51562500), Rocket::Core::Vector2f(0.97265625, 0.82812500))
72 };
73 
Invader(Game * _game,InvaderType _type,int _index)74 Invader::Invader(Game* _game, InvaderType _type, int _index) : position(0,0)
75 {
76 	type = UNKNOWN;
77 	animation_frame = 0;
78 	bomb_animation_frame = 0;
79 	bomb_frame_start = 0;
80 	death_time = 0;
81 	state = ALIVE;
82 	bomb = NONE;
83 	game = _game;
84 	type = _type;
85 	death_time = 0;
86 	invader_index = _index;
87 
88 	bomb_probability = GameDetails::GetDifficulty() == GameDetails::EASY ? BOMB_PROBABILITY_EASY : BOMB_PROBABILITY_HARD;
89 	bomb_probability *= type;
90 }
91 
~Invader()92 Invader::~Invader()
93 {
94 }
95 
SetPosition(const Rocket::Core::Vector2f & _position)96 void Invader::SetPosition(const Rocket::Core::Vector2f& _position)
97 {
98 	position = _position;
99 }
100 
GetPosition() const101 const Rocket::Core::Vector2f& Invader::GetPosition() const
102 {
103 	return position;
104 }
105 
Update()106 void Invader::Update()
107 {
108 	// Update the bombs
109 	if (Shell::GetElapsedTime() - bomb_frame_start > BOMB_UPDATE_FREQ)
110 	{
111 
112 		// Update the bomb position if its in flight, or check if we should drop one
113 		if (bomb != NONE)
114 		{
115 			if (bomb == RAY)
116 			{
117 				bomb_animation_frame++;
118 				if (bomb_animation_frame > 3)
119 					bomb_animation_frame = 0;
120 
121 				bomb_position.y += BOMB_RAY_SPEED;
122 			}
123 			else
124 			{
125 				bomb_position.y += BOMB_MISSILE_SPEED;
126 			}
127 
128 			if (bomb_position.y > game->GetWindowDimensions().y)
129 				bomb = NONE;
130 
131 			// Check if we hit the shields
132 			for (int i = 0; i < game->GetNumShields(); i++)
133 			{
134 				if (game->GetShield(i)->CheckHit(bomb_position))
135 				{
136 					bomb = NONE;
137 					break;
138 				}
139 			}
140 
141 			// Check if we hit the defender
142 			if (bomb != NONE)
143 			{
144 				if (game->GetDefender()->CheckHit(bomb_position))
145 					bomb = NONE;
146 			}
147 		}
148 		else if (state == ALIVE &&
149 				 Rocket::Core::Math::RandomReal(1.0f) < bomb_probability &&
150 				 game->CanDropBomb(invader_index))
151 		{
152 			bomb = Rocket::Core::Math::RandomInteger(2) == 0 ? RAY : MISSILE;
153 			bomb_position = position;
154 			bomb_position.x += invader_sprites[GetSpriteIndex()].dimensions.x / 2;
155 
156 			if (bomb == RAY)
157 				bomb_animation_frame = 0;
158 			else
159 				bomb_animation_frame = 4;
160 		}
161 
162 		bomb_frame_start = Shell::GetElapsedTime();
163 	}
164 
165 	if (state == EXPLODING && Shell::GetElapsedTime() > death_time)
166 		state = DEAD;
167 }
168 
UpdateAnimation()169 void Invader::UpdateAnimation()
170 {
171 	switch (state)
172 	{
173 	case ALIVE:
174 		animation_frame++;
175 		if (animation_frame > 1)
176 			animation_frame = 0;
177 		break;
178 
179 	default:
180 		break;
181 	}
182 }
183 
Render()184 void Invader::Render()
185 {
186 	if (type == MOTHERSHIP)
187 	{
188 		glColor4ubv(MOTHERSHIP_COLOUR);
189 	}
190 	int sprite_index = GetSpriteIndex();
191 	int sprite_offset = Rocket::Core::Math::RealToInteger((invader_sprites[sprite_index].dimensions.x - 48) / 2);
192 
193 	if (state != DEAD)
194 		invader_sprites[sprite_index].Render(Rocket::Core::Vector2f(position.x - sprite_offset, position.y));
195 
196 	if (bomb != NONE)
197 	{
198 		bomb_sprites[bomb_animation_frame].Render(bomb_position);
199 	}
200 
201 	if (type == MOTHERSHIP)
202 	{
203 		glColor4ub(255, 255, 255, 255);
204 	}
205 }
206 
GetState()207 Invader::InvaderState Invader::GetState()
208 {
209 	return state;
210 }
211 
CheckHit(const Rocket::Core::Vector2f & check_position)212 bool Invader::CheckHit(const Rocket::Core::Vector2f& check_position)
213 {
214 	// Get the sprite index we're currently using for collision detection
215 	int sprite_index = GetSpriteIndex();
216 	int sprite_offset = Rocket::Core::Math::RealToInteger((invader_sprites[sprite_index].dimensions.x - 48) / 2);
217 	float sprite_width = invader_sprites[sprite_index].dimensions.x;
218 	float sprite_height = invader_sprites[sprite_index].dimensions.y;
219 
220 	// If we're alive and the position is within our bounds, set ourselves
221 	// as exploding and return a valid hit
222 	if (state == ALIVE
223 		&& check_position.x >= position.x - sprite_offset
224 		&& check_position.x <= position.x - sprite_offset + sprite_width
225 		&& check_position.y >= position.y
226 		&& check_position.y <= position.y + sprite_height)
227 	{
228 		int score = 0;
229 		switch (type)
230 		{
231 			ROCKET_UNUSED_SWITCH_ENUM(UNKNOWN);
232 			case MOTHERSHIP: score = (Rocket::Core::Math::RandomInteger(6) + 1) * 50; break;	// 50 -> 300
233 			case RANK3: score = 40; break;
234 			case RANK2: score = 20; break;
235 			case RANK1: score = 10; break;
236 		}
237 
238 		// Add the number of points
239 		game->AddScore(score);
240 
241 		// Set our state to exploding and start the timer to our doom
242 		state = EXPLODING;
243 		death_time = Shell::GetElapsedTime() + EXPLOSION_TIME;
244 
245 		return true;
246 	}
247 
248 	return false;
249 }
250 
GetSpriteIndex() const251 int Invader::GetSpriteIndex() const
252 {
253 	// Calculate our sprite index based on animation and type
254 	int index = animation_frame;
255 	switch (type)
256 	{
257 		ROCKET_UNUSED_SWITCH_ENUM(UNKNOWN);
258 		case RANK1:                 break;      // animation_frame is the right index already
259 		case RANK2:	index += 2; break;
260 		case RANK3:	index += 4; break;
261 		case MOTHERSHIP: index = 6; break;
262 	}
263 
264 	// If we're in exploding state, use the exploding sprite
265 	if (state == EXPLODING)
266 		index = 7;
267 
268 	return index;
269 }
270