1 //  Ship.cpp -- The player's ship.
2 //  Copyright (C) 2008  Nick Gasson
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 //
17 
18 #include "Ship.hpp"
19 #include "OpenGL.hpp"
20 
21 #include <string>
22 #include <cmath>
23 
24 //
25 // Defines a simplified polygon representing the ship.
26 //
27 const Point Ship::hotspots[] = {
28    {2, 31}, {2, 26}, {4, 14}, {16, 0},
29    {29, 14}, {31, 26}, {31, 31}, {17, 31} };
30 
31 
Ship(Viewport * v)32 Ship::Ship(Viewport* v)
33    : shipImage("images/ship.png"),
34      xpos(0), ypos(0), speedX(0), speedY(0), angle(0), viewport(v),
35      thrusting(false),
36      boingSound(LocateResource("sounds/boing1.wav"))
37 {
38 
39 }
40 
Display() const41 void Ship::Display() const
42 {
43    int dx = (int)xpos - viewport->GetXAdjust();
44    int dy = (int)ypos - viewport->GetYAdjust();
45 
46    shipImage.Draw(dx, dy, angle);
47 }
48 
DrawExhaust()49 void Ship::DrawExhaust()
50 {
51    exhaust.Draw((float)viewport->GetXAdjust(),
52                 (float)viewport->GetYAdjust());
53 }
54 
DrawExplosion()55 void Ship::DrawExplosion()
56 {
57    explosion.Draw((float)viewport->GetXAdjust(),
58                   (float)viewport->GetYAdjust());
59 }
60 
Move()61 void Ship::Move()
62 {
63    RotatePoints(hotspots, points, NUM_HOTSPOTS, angle*M_PI/180, -16, 16);
64 
65     xpos += speedX;
66     ypos += speedY;
67 
68     // Check bounds
69     if (xpos <= 0.0) {
70        xpos = 0.0;
71        speedX *= -0.5;
72         boingSound.Play();
73     }
74     else if (xpos + shipImage.GetWidth() > viewport->GetLevelWidth()) {
75        xpos = (double)(viewport->GetLevelWidth() - shipImage.GetWidth());
76        speedX *= -0.5;
77        boingSound.Play();
78     }
79     if (ypos <= 0.0) {
80        ypos = 0.0;
81        speedY *= -0.5;
82        boingSound.Play();
83     }
84     else if (ypos + shipImage.GetHeight() > viewport->GetLevelHeight()) {
85        ypos = (double)(viewport->GetLevelHeight() - shipImage.GetHeight());
86        speedY *= -0.5;
87        boingSound.Play();
88     }
89 
90     exhaust.xpos = xpos + shipImage.GetWidth()/2
91        - (shipImage.GetWidth()/2)*sin(angle*(M_PI/180));
92     exhaust.ypos = ypos + shipImage.GetHeight()/2
93        + (shipImage.GetHeight()/2)*cos(angle*(M_PI/180));
94 
95     const float SCALE = 1.0f;
96     exhaust.yi_bias = SCALE * cosf(angle*M_PI/180) + speedY;
97     exhaust.xi_bias = SCALE * -sinf(angle*M_PI/180) + speedX;
98 
99     explosion.xpos = xpos + shipImage.GetWidth()/2;
100     explosion.ypos = ypos + shipImage.GetHeight()/2;
101 }
102 
ProcessEffects(bool paused,bool exploding)103 void Ship::ProcessEffects(bool paused, bool exploding)
104 {
105    exhaust.Process(thrusting, !paused);
106    explosion.Process(exploding);
107 }
108 
ThrustOn()109 void Ship::ThrustOn()
110 {
111    thrusting = true;
112 }
113 
ThrustOff()114 void Ship::ThrustOff()
115 {
116    thrusting = false;
117 }
118 
Thrust(double speed)119 void Ship::Thrust(double speed)
120 {
121    speedX += speed * sin(angle*(M_PI/180));
122    speedY -= speed * cos(angle*(M_PI/180));
123 }
124 
Turn(double delta)125 void Ship::Turn(double delta)
126 {
127    angle += delta;
128 }
129 
ApplyGravity(double gravity)130 void Ship::ApplyGravity(double gravity)
131 {
132    speedY += gravity;
133 }
134 
Bounce()135 void Ship::Bounce()
136 {
137    speedX *= -1;
138    speedY *= -1;
139    speedX /= 2;
140    speedY /= 2;
141 }
142 
CentreInViewport()143 void Ship::CentreInViewport()
144 {
145    int centrex = (int)xpos + (shipImage.GetWidth()/2);
146    int centrey = (int)ypos + (shipImage.GetHeight()/2);
147    OpenGL& opengl = OpenGL::GetInstance();
148    viewport->SetXAdjust(centrex - (opengl.GetWidth()/2));
149    viewport->SetYAdjust(centrey - (opengl.GetHeight()/2));
150 }
151 
152 //
153 // Reset at the start of a new level.
154 //
Reset()155 void Ship::Reset()
156 {
157    exhaust.Reset();
158    explosion.Reset();
159 
160    xpos = (double)viewport->GetLevelWidth()/2;
161    ypos = SHIP_START_Y - 40;
162 
163    angle = 0.0f;
164    speedX = 0.0f;
165    speedY = 0.0f;
166 }
167 
RotatePoints(const Point * pPoints,Point * pDest,int nCount,double angle,int adjustx,int adjusty)168 void Ship::RotatePoints(const Point* pPoints, Point* pDest, int nCount,
169                         double angle, int adjustx, int adjusty)
170 {
171    for (int i = 0; i < nCount; i++) {
172       int x = pPoints[i].x + adjustx;
173       int y = pPoints[i].y*-1 + adjusty;
174       pDest[i].x = (int)(x*cos(angle)) + (int)(y*sin(angle));
175       pDest[i].y = (int)(y*cos(angle)) - (int)(x*sin(angle));
176       pDest[i].y -= adjusty;
177       pDest[i].x -= adjustx;
178       pDest[i].y *= -1;
179    }
180 }
181 
182 //
183 // Check for collision between the ship and a polygon.
184 //
HotSpotCollision(LineSegment & l,double dx,double dy) const185 bool Ship::HotSpotCollision(LineSegment& l, double dx, double dy) const
186 {
187    for (int i = 0; i < NUM_HOTSPOTS; i++) {
188       if (CheckCollision(l, dx + points[i].x, dy + points[i].y))
189          return true;
190    }
191 
192    return false;
193 }
194 
195 //
196 // Checks for collision between the ship and a box.
197 //
BoxCollision(int x,int y,int w,int h) const198 bool Ship::BoxCollision(int x, int y, int w, int h) const
199 {
200    if (!viewport->PointInScreen(x, y, w, h))
201       return false;
202 
203    LineSegment l1(x, y, x + w, y);
204    LineSegment l2(x + w, y, x + w, y + h);
205    LineSegment l3(x + w, y + h, x, y + h);
206    LineSegment l4(x, y + h, x, y);
207 
208    return HotSpotCollision(l1) || HotSpotCollision(l2)
209       || HotSpotCollision(l3) || HotSpotCollision(l4);
210 }
211 
212 //
213 // Checks for collision between the ship and a line segment.
214 //
CheckCollision(LineSegment & l,double dx,double dy) const215 bool Ship::CheckCollision(LineSegment& l, double dx, double dy) const
216 {
217    double xpos = this->xpos + dx;
218    double ypos = this->ypos + dy;
219 
220    if (!viewport->PointInScreen
221        ((int)xpos, (int)ypos, shipImage.GetWidth(), shipImage.GetHeight()))
222       return false;
223 
224    // Get position after next move
225    double cX = xpos + speedX;
226    double cY = ypos + speedY;
227 
228    // Get displacement
229    double vecX = cX - xpos;
230    double vecY = cY - ypos;
231 
232    // Get line position
233    double wallX = (double)(l.p2.x - l.p1.x);
234    double wallY = (double)(l.p2.y - l.p1.y);
235 
236    // Work out numerator and denominator (used parametric equations)
237    double numT = wallX * (ypos - l.p1.y) - wallY * (xpos - l.p1.x);
238    double numU = vecX * (ypos - l.p1.y) - vecY * (xpos - l.p1.x);
239 
240    // Work out denominator
241    double denom = wallY * (cX - xpos) - wallX * (cY - ypos);
242 
243    // Work out u and t
244    double u = numU / denom;
245    double t = numT / denom;
246 
247    // Collision occured if (0 < t < 1) and (0 < u < 1)
248    return (t > 0.0f) && (t < 1.0f) && (u > 0.0f) && (u < 1.0f);
249 }
250 
251