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