1 /*
2 * Kuklomenos
3 * Copyright (C) 2008-2009 Martin Bays <mbays@sdf.lonestar.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/.
17 */
18
19 #include "node.h"
20 #include "invaders.h"
21 #include "random.h"
22 #include "sound.h"
23 #include "coords.h"
24
Node(RelPolarCoord pos,float ds,NodeColour nodeColour,float spinRate,Angle spin,int pitch,float radius)25 Node::Node(RelPolarCoord pos, float ds, NodeColour nodeColour, float spinRate,
26 Angle spin, int pitch, float radius) :
27 HPInvader(1,2),
28 SpirallingPolygonalInvader(3, pos, ds, 0),
29 sparkPoint(rani(3)),
30 pitch(pitch),
31 radius(radius),
32 spinRate(spinRate),
33 spin(spin),
34 nodeColour(nodeColour),
35 status(NODEST_NONE), primed(0), primeRate(0),
36 targettingInfester(NULL), extractionProgress(0)
37 {
38 setPoints();
39 setSparks();
40 }
41
setPoints()42 void Node::setPoints()
43 {
44 for (int i = 0; i < 3; i++)
45 points[i] = RelPolarCoord(spin + i*4.0/3, radius);
46 }
47
doUpdate(int time)48 void Node::doUpdate(int time)
49 {
50 SpirallingInvader::doUpdate(time);
51
52 if (status == NODEST_YOU && primed < 1)
53 primed += primeRate*0.001*time;
54 else if (status == NODEST_NONE && primed > 0)
55 primed = std::max(0.0, primed - 0.005*time);
56
57 if (status == NODEST_NONE || status == NODEST_YOU)
58 {
59 if (fabs(angleDiff(spin*3, 0)) < fabs(time*spinRate*3))
60 {
61 soundEvents.newEvent(pos, nodeHumChunk,
62 48, pitch, true);
63 if (status == NODEST_YOU)
64 {
65 // harmonies
66 soundEvents.newEvent(pos, nodeHumChunk,
67 48, int(pitch*4/5), true);
68 soundEvents.newEvent(pos, nodeHumChunk,
69 48, int(pitch*2/3), true);
70 }
71 }
72
73 if (primed >= 1)
74 {
75 // c.f. Node::glowPhase()
76 if (fabs(angleDiff(spin*6, 0)) < fabs(time*spinRate*6))
77 soundEvents.newEvent(pos, primedChunk, 48, pitch);
78 }
79
80 spin += time*spinRate;
81 setPoints();
82 }
83 else if (status == NODEST_EVIL)
84 {
85 // turn to point directly away from centre:
86 spin -= (angleDiff(0,spin*3)*time/3000);
87 setPoints();
88 }
89 }
90
extract(int time,bool hasCyan)91 int Node::extract(int time, bool hasCyan)
92 {
93 int extracted = 0;
94
95 extractionProgress += (hasCyan ? 2.5 : 1.0)*0.001*time;
96
97 while (extractionProgress > 1)
98 {
99 extractionProgress--;
100 extracted++;
101 soundEvents.newEvent(pos, sparkChunk,
102 48 + int(16*gaussian()),
103 pitch + int(200*gaussian()));
104 }
105
106 if (extracted)
107 setSparks();
108
109 return extracted;
110 }
111
setSparks()112 void Node::setSparks()
113 {
114 const int numSparkVertices = 3+rani(3);
115 sparkVertices.clear();
116 sparkPoint = rani(3);
117 RelPolarCoord vertex = RelPolarCoord(0, dist(points[0]));
118 sparkVertices.push_back(vertex);
119 for (int i = 1; i < numSparkVertices - 1; i++)
120 {
121 vertex = RelPolarCoord(
122 vertex.angle + (-0.1+ranf(0.2))*i,
123 vertex.dist -
124 (0.5+ranf(0.5))*(vertex.dist/(numSparkVertices-i)));
125 sparkVertices.push_back(vertex);
126 }
127 sparkVertices.push_back(RelPolarCoord(0,0));
128 }
129
getSparkVertex(int v) const130 CartCoord Node::getSparkVertex(int v) const
131 {
132 return cpos() + sparkVertices[v].rotated(
133 pos.angle + angle(points[sparkPoint]));
134 }
135
glowPhase() const136 Angle Node::glowPhase() const
137 {
138 return 6*spin;
139 }
140
colour() const141 Uint32 Node::colour() const
142 {
143 Uint32 col;
144 switch (nodeColour)
145 {
146 case NODEC_RED:
147 col = 0x01000000;
148 break;
149 case NODEC_YELLOW:
150 col = 0x01010000;
151 break;
152 case NODEC_GREEN:
153 col = 0x00010000;
154 break;
155 case NODEC_BLUE:
156 col = 0x00000100;
157 break;
158 case NODEC_PURPLE:
159 col = 0x01000100;
160 break;
161 case NODEC_CYAN:
162 col = 0x00010100;
163 break;
164 default:
165 col = 0x01010100;
166 }
167
168 int intensity = 0;
169
170 if (status == NODEST_NONE || status == NODEST_YOU)
171 {
172 if ( primed >= 1 )
173 intensity = 0xff - (int)(0x30 * (1 - glowPhase().sinf()));
174 else
175 {
176 intensity = (status == NODEST_NONE ? 0x80 : 0xbf);
177 intensity += int( (0xff - intensity) *
178 std::max(0.0f, 1 - ( fabsf(angleDiff(spin*3, 0)) * 2 )) );
179 }
180 }
181 else
182 intensity = status == NODEST_DESTROYED ? 0x20 :
183 status == NODEST_EVIL ? 0xbf :
184 0x00;
185 return col*0xff + intensity;
186 }
187
innerColour() const188 Uint32 Node::innerColour() const
189 {
190 return 0;
191 }
192
draw(SDL_Surface * surface,const View & view,View * boundView,bool noAA) const193 void Node::draw(SDL_Surface* surface, const View& view, View*
194 boundView, bool noAA) const
195 {
196
197 if (primed > 0)
198 {
199 // prongs
200 float prongStage = std::min(1.0f, primed*3);
201 float prongStart = std::max(0.0f, primed*20 - 19);
202
203 if (prongStage < 1)
204 Circle(cpos(), 2.0*(1-prongStage),
205 0x0000c000 + (int)(0x60*(1-prongStage)),
206 true).draw(surface, view, boundView, noAA);
207
208 if (prongStart < 1)
209 for (int i = 0; i < 3; i++)
210 {
211 Line(cpos() + points[i].rotated(pos.angle) * prongStart,
212 cpos() + points[i].rotated(pos.angle) * prongStage,
213 0x00ffff40+(int)(0x60*prongStage)
214 ).draw(surface, view, boundView, noAA);
215 }
216
217 CartCoord tpoints[3];
218 for (int i = 0; i < 3; i++)
219 tpoints[i] = cpos() + points[i].rotated(pos.angle) * primed;
220
221 Polygon(tpoints, 3, 0x00ffff00 +
222 ((primed >= 1) ? (0xff - (int)(0x30 * (1 - glowPhase().sinf()))) :
223 (int)(0x40 + 0x60*primed)),
224 true).draw(surface, view, boundView, noAA);
225 if (primed < 1)
226 Polygon(tpoints, 3, 0x00ffffa0,
227 false).draw(surface, view, boundView, noAA);
228 }
229
230 SpirallingPolygonalInvader::draw(surface, view, boundView, noAA);
231
232 if (status == NODEST_EVIL)
233 {
234 static const float glintSep = ARENA_RAD/15.0;
235 float glintDist = pos.dist + glintSep*extractionProgress;
236 while (glintDist < ARENA_RAD)
237 {
238 const CartCoord glintPos = ARENA_CENTRE + RelPolarCoord(pos.angle,
239 glintDist);
240
241 Pixel(glintPos, 0x00ffffff
242 ).draw(surface, view, boundView, noAA);
243
244 glintDist += glintSep;
245 }
246 if (extractionProgress > 0.8)
247 {
248 const int numVertices = sparkVertices.size();
249 const int sparkFrom = (int)(
250 (extractionProgress-0.8)*numVertices/0.2);
251 if (sparkFrom < numVertices - 1)
252 Line(getSparkVertex(sparkFrom),
253 getSparkVertex(sparkFrom + 1),
254 0x00ffffe0
255 ).draw(surface, view, boundView, noAA);
256 else
257 Pixel(getSparkVertex(sparkFrom), 0x00ffffff
258 ).draw(surface, view, boundView, noAA);
259
260 if (sparkFrom > 0 && sparkFrom < numVertices)
261 Line(getSparkVertex(sparkFrom - 1),
262 getSparkVertex(sparkFrom),
263 0x00ffff90
264 ).draw(surface, view, boundView, noAA);
265 }
266 }
267 }
268
infest(InfestingInvader * inv)269 bool Node::infest(InfestingInvader* inv)
270 {
271 if (status == NODEST_DESTROYED || status == NODEST_EVIL)
272 return false;
273 if (status == NODEST_YOU)
274 {
275 uncapture();
276 return false;
277 }
278 status = NODEST_EVIL;
279
280 return true;
281 }
uninfest()282 void Node::uninfest()
283 {
284 if (status == NODEST_EVIL)
285 status = NODEST_NONE;
286 }
capture(CapturePod * pod)287 bool Node::capture(CapturePod* pod)
288 {
289 if (status != NODEST_NONE)
290 return false;
291
292 status = NODEST_YOU;
293 primeRate = pod->primeRate;
294 return true;
295 }
uncapture()296 void Node::uncapture()
297 {
298 status = NODEST_NONE;
299 }
300
hit(int weight)301 int Node::hit(int weight)
302 {
303 if (weight >= 3 && status == NODEST_YOU && primed >= 1)
304 {
305 uncapture();
306 status = NODEST_DESTROYED;
307 primed = 0;
308 return 3;
309 }
310
311 return weight;
312 }
313