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