1 /* c_combatant.cc 1.22 95/12/24 23:23:55 */
2
3
4 // xspacewarp by Greg Walker (gow@math.orst.edu)
5
6 // This is free software. Non-profit redistribution and/or modification
7 // is allowed and welcome.
8
9
10 // combatant methods. contains the complicated faser/torpedo methods.
11 // this version fires fasers in a long continuous line rather than pulses
12
13 #include "c_combatant.hh"
14 #include <X11/Intrinsic.h>
15 #include <X11/StringDefs.h>
16 #include "common.hh"
17 #include "params.hh"
18 #include "globals.hh" // toplevel for display
19 #include "c_sector.hh"
20 #include "space_objects.hh"
21
22
23 // return percentage levels of thrusters, warpdrive, fasers and shields.
24
getthrusters() const25 int Combatant::getthrusters() const
26 {
27 return ((int)(((float)100 * (float)energy.thrusters) / (float)getmaxerg()));
28 }
29
30
getwarpdrive() const31 int Combatant::getwarpdrive() const
32 {
33 return ((int)(((float)100 * (float)energy.warpdrive) / (float)getmaxerg()));
34 }
35
36
getfasers() const37 int Combatant::getfasers() const
38 {
39 return ((int)(((float)100 * (float)energy.fasers) / (float)getmaxerg()));
40 }
41
42
getshields() const43 int Combatant::getshields() const
44 {
45 return ((int)(((float)100 * (float)energy.shields) / (float)getmaxerg()));
46 }
47
48
49 // The faser/torpedo timeouts, faser_to() and torpedo_to(), call
50 // moveshot() every "interval" milliseconds. moveshot() resets
51 // the timer and passes the "this" pointer as client_data. Must
52 // call initshot() before moveshot().
53
54
moveshot(int length,int margin,unsigned long interval,GC gc,GC gc_rv,XtTimerCallbackProc proc)55 void Combatant::moveshot(int length, int margin,
56 unsigned long interval, GC gc, GC gc_rv,
57 XtTimerCallbackProc proc)
58 {
59 Point pnt, oldto;
60 HitReport hitrep; // describe anything that got hit
61 long sqmag1, sqmag2; // squares of vector magnitudes
62
63 // update shot.to, but keep it fixed if shot.fixend is true.
64
65 if (!shot.fixend)
66 {
67 oldto = shot.to; // save old value
68 shot.f_to.x = shot.f_to.x + shot.delta.x; // compute new positions with
69 shot.f_to.y = shot.f_to.y + shot.delta.y; // floats to resolve 1 deg angles
70 shot.to.x = (int) shot.f_to.x;
71 shot.to.y = (int) shot.f_to.y;
72 hitrep = universe[urow-1][ucol-1].detect_hit(shot);
73 switch (hitrep.what) // what got hit
74 {
75 case NOTHING:
76 if (snip(margin)) // snip the float positions too.
77 {
78 shot.f_to.x = (float) shot.to.x;
79 shot.f_to.y = (float) shot.to.y;
80 }
81 break;
82 case STAR:
83 mustmove = true; // flag used by jovians for ai decisions
84 shot.fixend = true;
85 shot.to = hitrep.newto;
86 shot.f_to.x = (float) shot.to.x;
87 shot.f_to.y = (float) shot.to.y;
88 break;
89 case BLACKHOLE:
90 shot.fixend = true;
91 shot.to = hitrep.newto;
92 shot.f_to.x = (float) shot.to.x;
93 shot.f_to.y = (float) shot.to.y;
94 break;
95 case JOVIAN:
96 mustmove = true;
97 // fall through
98 case BASE:
99 case ENDEVER:
100 if (hitrep.damage == FATAL) // done
101 {
102 shot.to = oldto;
103 if (visible())
104 cleanup(gc_rv);
105 shot.inprogress = false;
106 return;
107 }
108 shot.fixend = true;
109 shot.to = hitrep.newto;
110 shot.f_to.x = (float) shot.to.x;
111 shot.f_to.y = (float) shot.to.y;
112 break;
113 }
114
115 if (visible())
116 {
117 sqmag1 = sqdist(shot.start, oldto);
118 sqmag2 = sqdist(shot.start, shot.to);
119 if (sqmag2 <= sqmag1) // newto closer to source than old "to"
120 {
121 // remove excess faser/torpedo crap if something crosses middle of shot
122 XDrawLine(DISPLAY, WINDOW, gc_rv, shot.to.x, shot.to.y,
123 oldto.x, oldto.y);
124 }
125 else
126 {
127 XDrawLine(DISPLAY, WINDOW, gc, oldto.x, oldto.y,
128 shot.to.x, shot.to.y);
129 }
130 }
131 }
132
133 if (shot.fixend || shot.weapon == TORPEDO)
134 {
135 // update shot.from
136
137 shot.f_from.x = shot.f_from.x + shot.delta.x;
138 shot.f_from.y = shot.f_from.y + shot.delta.y;
139 pnt.x = (int) shot.f_from.x;
140 pnt.y = (int) shot.f_from.y;
141
142 sqmag1 = sqdist(shot.start, shot.to);
143 sqmag2 = sqdist(shot.start, pnt);
144 if (sqmag2 >= sqmag1) // shot done
145 {
146 if (visible())
147 cleanup(gc_rv);
148 shot.inprogress = false;
149 return;
150 }
151
152 // Erase tail of shot. To avoid stray lit pixels, erase 3 pixels inward
153 // towards the center of the shooting source.
154
155 if (visible())
156 {
157 XDrawLine(DISPLAY, XtWindow(widget), gc_rv,
158 shot.from.x - (int)(3*shot.cos),
159 shot.from.y + (int)(3*shot.sin), pnt.x, pnt.y);
160 }
161 shot.from = pnt;
162 }
163
164 // set timer for next call on this function
165
166 shot.id = XtAppAddTimeOut(app_context, interval,
167 (XtTimerCallbackProc) proc,
168 (XtPointer) this);
169 }
170
171
172 // Initialize Shot struct and start timeout timer.
173
initshot(int length,int margin,unsigned long interval,GC gc,GC gc_rv,XtTimerCallbackProc proc)174 void Combatant::initshot(int length, int margin, unsigned long interval,
175 GC gc, GC gc_rv, XtTimerCallbackProc proc)
176 {
177 long from_sqmag, to_sqmag; // squares of magnitudes of the vectors
178 // start->from and start->to
179 HitReport hitrep;
180
181 // initialize shot.fixend
182
183 shot.fixend = false;
184
185 // determine position for tail of shot
186
187 shot.f_from.x = (float)shot.start.x + (float)RADIUS * shot.cos;
188 shot.f_from.y = (float)shot.start.y - (float)RADIUS * shot.sin;
189 shot.from.x = (int) shot.f_from.x;
190 shot.from.y = (int) shot.f_from.y;
191
192 // do not shoot anything if ship too close to sector border
193
194 if ((shot.from.x < SECTMINX + margin) ||
195 (shot.from.x > SECTMAXX - margin) ||
196 (shot.from.y < SECTMINY + margin) ||
197 (shot.from.y > SECTMAXY - margin))
198 {
199 shot.inprogress = false;
200 return;
201 }
202
203 // determine position for head of shot
204
205 shot.f_to.x = shot.f_from.x + (float)length * shot.cos;
206 shot.f_to.y = shot.f_from.y - (float)length * shot.sin;
207 shot.to.x = (int) shot.f_to.x;
208 shot.to.y = (int) shot.f_to.y;
209
210 // check if hits and if necessary, truncate line and set shot.fixend to true
211
212 hitrep = universe[urow-1][ucol-1].detect_hit(shot);
213 switch (hitrep.what)
214 {
215 case NOTHING:
216 if (snip(margin)) // snip the float positions too.
217 {
218 shot.f_to.x = (float) shot.to.x;
219 shot.f_to.y = (float) shot.to.y;
220 }
221 break;
222 case STAR:
223 mustmove = true;
224 shot.fixend = true;
225 shot.to = hitrep.newto;
226 shot.f_to.x = (float) shot.to.x;
227 shot.f_to.y = (float) shot.to.y;
228 break;
229 case BLACKHOLE:
230 shot.fixend = true;
231 shot.to = hitrep.newto;
232 shot.f_to.x = (float) shot.to.x;
233 shot.f_to.y = (float) shot.to.y;
234 break;
235 case JOVIAN:
236 mustmove = true;
237 // fall through
238 case BASE:
239 case ENDEVER:
240 shot.fixend = true;
241 if (hitrep.damage == FATAL) // done
242 {
243 shot.inprogress = false;
244 return;
245 }
246 shot.to = hitrep.newto;
247 shot.f_to.x = (float) shot.to.x;
248 shot.f_to.y = (float) shot.to.y;
249 break;
250 }
251
252 // finished if "to" end is closer to shot source than "from" end
253
254 from_sqmag = sqdist(shot.start, shot.from);
255 to_sqmag = sqdist(shot.start, shot.to);
256 if (to_sqmag <= from_sqmag)
257 {
258 shot.inprogress = false;
259 return;
260 }
261
262 if (visible())
263 {
264 XDrawLine(DISPLAY, WINDOW, gc,
265 shot.from.x, shot.from.y,
266 shot.to.x, shot.to.y);
267 }
268 shot.id = XtAppAddTimeOut(app_context, interval,
269 (XtTimerCallbackProc) proc,
270 (XtPointer) this);
271 }
272
273
274
275 // snip the segment shot.from -> shot.to short by changing
276 // shot.to if the segment crosses into the margin with thickness
277 // "margin" surrounding the inside of the sector border. Return
278 // true if snip() changed the value of shot.to and return false
279 // otherwise. snip() sets the shot.fixend member to true if the
280 // line gets snipped and does nothing to shot.fixend otherwise.
281
snip(int margin)282 bool Combatant::snip(int margin)
283 {
284 bool val = false;
285 Point pnt;
286
287 // "if" statements are done in sequence to handle the case where
288 // the line gets too close to a corner.
289
290 if (shot.to.x > SECTMAXX-margin) // hit right boundary
291 {
292 pnt.x = SECTMAXX-margin;
293 pnt.y = (int)((float)(pnt.x - shot.from.x)*
294 ((float)(shot.to.y - shot.from.y)/
295 (float)(shot.to.x - shot.from.x))) + shot.from.y;
296 shot.to = pnt;
297 shot.fixend = true;
298 val = true;
299 }
300 if (shot.to.x < SECTMINX+margin) // hit left boundary
301 {
302 pnt.x = SECTMINX+margin;
303 pnt.y = (int)((float)(pnt.x - shot.from.x)*
304 ((float)(shot.to.y - shot.from.y)/
305 (float)(shot.to.x - shot.from.x))) + shot.from.y;
306 shot.to = pnt;
307 shot.fixend = true;
308 val = true;
309 }
310 if (shot.to.y < SECTMINY+margin) // hit top boundary
311 {
312 pnt.y = SECTMINY+margin;
313 pnt.x = (int)((float)(pnt.y - shot.from.y)*
314 ((float)(shot.to.x - shot.from.x)/
315 (float)(shot.to.y - shot.from.y))) + shot.from.x;
316 shot.to = pnt;
317 shot.fixend = true;
318 val = true;
319 }
320 if (shot.to.y > SECTMAXY-margin) // hit bottom boundary
321 {
322 pnt.y = SECTMAXY-margin;
323 pnt.x = (int)((float)(pnt.y - shot.from.y)*
324 ((float)(shot.to.x - shot.from.x)/
325 (float)(shot.to.y - shot.from.y))) + shot.from.x;
326 shot.to = pnt;
327 shot.fixend = true;
328 val = true;
329 }
330
331 return (val);
332 }
333
334
335 // erase a shot and remove any stray pixels. pass a reverse video GC.
336
cleanup(GC gc_rv)337 void Combatant::cleanup(GC gc_rv)
338 {
339 Point nw, se; // nw, se corners of cleanup rectangle
340
341 XDrawLine(DISPLAY, WINDOW, gc_rv,
342 shot.from.x - (int)(3*shot.cos),
343 shot.from.y + (int)(3*shot.sin),
344 shot.to.x, shot.to.y);
345
346 // remove stray pixels around shot.to
347
348 nw.x = max(shot.to.x - CLEANUPW/2, SECTMINX);
349 nw.y = max(shot.to.y - CLEANUPH/2, SECTMINY);
350 se.x = min(shot.to.x + CLEANUPW/2, SECTMAXX);
351 se.y = min(shot.to.y + CLEANUPH/2, SECTMAXY);
352 XCopyArea(DISPLAY, pixmap, XtWindow(widget), def_GC,
353 nw.x, nw.y, se.x - nw.x + 1, se.y - nw.y + 1, nw.x, nw.y);
354 }
355
356 // end
357