xref: /netbsd-src/games/warp/weapon.c (revision 1182a44c59cae4d586117d55eca24b4b8b173211)
1 /* Header: weapon.c,v 7.0.1.2 86/10/20 14:36:33 lwall Exp */
2 
3 /* Log:	weapon.c,v
4  * Revision 7.0.1.2  86/10/20  14:36:33  lwall
5  * Picked some lint.
6  *
7  * Revision 7.0.1.1  86/10/16  10:54:42  lwall
8  * Added Damage.  Fixed random bugs.
9  *
10  * Revision 7.0  86/10/08  15:18:08  lwall
11  * Split into separate files.  Added amoebas and pirates.
12  *
13  */
14 
15 #include "EXTERN.h"
16 #include "warp.h"
17 #include "bang.h"
18 #include "object.h"
19 #include "move.h"
20 #include "score.h"
21 #include "sig.h"
22 #include "term.h"
23 #include "them.h"
24 #include "us.h"
25 #include "util.h"
26 #include "INTERN.h"
27 #include "weapon.h"
28 
29 void
weapon_init(void)30 weapon_init(void)
31 {
32     ;
33 }
34 
35 void
fire_torp(OBJECT * from,int ydir,int xdir)36 fire_torp(OBJECT *from, int ydir, int xdir)
37 {
38     OBJECT *to;
39 
40     if (from->type == Enemy ||
41        (from == ent && etorp > 0) ||
42        (from == base && btorp > 0)) {
43 	to = occupant[(from->posy+from->vely+ydir+YSIZE00)%YSIZE]
44 		     [(from->posx+from->velx+xdir+XSIZE00)%XSIZE];
45 	if (from->type != Enemy || !to || to->vely || to->velx) {
46 	    if (from->type != Enemy &&
47 		 (to = isatorp[from==base][ydir+1][xdir+1])) {
48 		to->vely += ydir;
49 		to->velx += xdir;
50 	    }
51 	    else {
52 		if (from == ent) {
53 		    to = make_object(Torp, '+', from->posy,from->posx,
54 			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
55 		    aretorps++;
56 		    isatorp[0][ydir+1][xdir+1] = to;
57 		    etorp--;
58 		}
59 		else if (from == base) {
60 		    to = make_object(Torp, '+', from->posy,from->posx,
61 			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
62 		    aretorps++;
63 		    isatorp[1][ydir+1][xdir+1] = to;
64 		    btorp--;
65 		}
66 		else if (from->image == 'G') {
67 		    numos++;
68 		    to = make_object(Torp, 'o', from->posy,from->posx,
69 			from->vely+ydir,from->velx+xdir, 100L, 1L,&root);
70 		    if (madgorns) {
71 			possiblescore += 35;
72 			to->image = '0';
73 			to->mass = 2000;
74 			to->energy = 2000;
75 		    }
76 		    else if (rand_mod(120)+10 > smarts)
77 			possiblescore += 100;
78 		    else {
79 			possiblescore += 200;
80 			to->image = 'O';
81 		    }
82 		}
83 		else {
84 		    to = make_object(Torp, 'x', from->posy,from->posx,
85 			from->vely+ydir,from->velx+xdir, 0L, 1L,&root);
86 		    if (rand_mod(160)+10 > smarts)
87 			possiblescore += 10;
88 		    else {
89 			possiblescore += 100;
90 			to->image = 'X';
91 			to->mass = 1000+super*20;
92 			numxes++;
93 		    }
94 		}
95 	    }
96 	}
97     }
98 }
99 
100 void
attack(OBJECT * attackee)101 attack(OBJECT *attackee)
102 {
103     int dx;
104     int dy;
105     int curx;
106     int cury;
107     int prob;
108     OBJECT *obj;
109     bool torps;
110     bool webnear = false;
111     bool thru_stars;
112     int nukey;
113     int nukex;
114     int nukedist;
115 
116     if (attackee) {
117 	if (attackee == nuke) {
118 	    if (amb[attackee->posy][attackee->posx] != '~')
119 		return;
120 	    nukey = nukex = 0;
121 	    nukedist = 100;
122 	}
123 	for (dx= -1; dx<=1 ; dx++) {
124 	    for (dy= -1; dy<=1; dy++) {
125 		if (dx||dy) {
126 		    cury = attackee->posy;
127 		    curx = attackee->posx;
128 		    torps = thru_stars = false;
129 		    if (massacre || madgorns || !rand_mod(53-super) )
130 			webnear += rand_mod(2);
131 		    else
132 			webnear = false;
133 		    for (prob = scandist;prob;prob--) {
134 			cury = (cury + dy + YSIZE00) % YSIZE;
135 			curx = (curx + dx + XSIZE00) % XSIZE;
136 			if ((obj = occupant[cury][curx]) != NULL) {
137 			    switch (obj->image) {
138 			    case 'P': case 'K': case 'R': case ' ':
139 		pot_shot:
140 				if (attackee == nuke) {
141 				    if (rand_mod(2+scandist-prob) <
142 				      rand_mod(smarts/40+1))
143 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
144 				}
145 				if (rand_mod(51 - sm50) <= prob) {
146 				    switch (obj->strategy||thru_stars?0:
147 					  rand_mod(ent?4:2)) {
148 				    case 1: case 2:
149 					if (-dy + attackee->vely == obj->vely
150 					 && -dx + attackee->velx == obj->velx)
151 					    fire_torp(obj,
152 					     -dy + attackee->vely,
153 					     -dx + attackee->velx);
154 					else
155 					    fire_torp(obj,
156 					     -dy + attackee->vely - obj->vely,
157 					     -dx + attackee->velx - obj->velx);
158 					if (obj->image == ' ')
159 					    setimage(obj,
160 					      obj->flags & PIRATE ? 'P' : 'R');
161 					break;
162 				    case 3: {
163 					int newspeed =
164 					    rand_mod(prob<5&&smarts>70?4:3)-1;
165 
166 					obj->vely = -dy * newspeed;
167 					obj->velx = -dx * newspeed;
168 					if (newspeed >= 0 &&
169 					    !rand_mod(82-sm80)) {
170 					    obj->vely += attackee->vely;
171 					    obj->velx += attackee->velx;
172 					}
173 					break;
174 				    }
175 				    case 0:
176 					if (!torps && obj->energy > 1000) {
177 					    fire_phaser(obj, -dy, -dx);
178 					    if (smarts > 40 &&
179 					       (scandist-prob > 5
180 						|| attackee==base) &&
181 					       (massacre || obj->strategy ||
182 					        rand_mod(2)))
183 						while (rand_mod(2))
184 						    fire_phaser(obj, -dy, -dx);
185 					    if (obj->image == ' ')
186 						setimage(obj,
187 						  obj->flags&PIRATE ? 'P':'R');
188 					}
189 					if (obj->strategy) {
190 					    obj->velx = obj->vely = 0;
191 					    if (obj->energy < 1000 ||
192 						  bvely || bvelx)
193 						obj->strategy = 0;
194 					}
195 					else if ((attackee==base ||
196 						  (cloaking && attackee==ent)
197 						 ) &&
198 						 scandist-prob > 5 &&
199 					         !(rand_mod(
200 						   ent?antibase*2:antibase)) )
201 					    obj->strategy = 1;
202 					break;
203 				    }
204 				}
205 				goto bombout;
206 			    case 'G':
207 				if (thru_stars && obj->strategy < 7)
208 				    goto bombout;
209 				if (attackee == nuke) {
210 				    if (rand_mod(2+scandist-prob) <
211 				      rand_mod(smarts/40+1))
212 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
213 				    goto bombout;
214 				}
215 				if (obj->strategy) {
216 				    if (madgorns || !rand_mod(4)) {
217 					obj->vely = attackee->vely;
218 					obj->velx = attackee->velx;
219 				    }
220 				    obj->strategy += (!torps && deados > 10);
221 				    if (obj->strategy > 4)
222 					madgorns = true;
223 				    if (!torps && obj->strategy > 5) {
224 					do {
225 					    fire_phaser(obj, -dy, -dx);
226 					} while (rand_mod(2));
227 				    }
228 				}
229 				else if (numgorns >= numenemies-1 &&
230 				    deados > 15+numgorns*5)
231 				    obj->strategy = 1;
232 				if (madgorns || rand_mod(51 - sm50) <= prob) {
233 				    if (-dy + attackee->vely == obj->vely
234 				     && -dx + attackee->velx == obj->velx)
235 					fire_torp(obj,
236 					 -dy + attackee->vely,
237 					 -dx + attackee->velx);
238 				    else
239 					fire_torp(obj,
240 					 -dy + attackee->vely - obj->vely,
241 					 -dx + attackee->velx - obj->velx);
242 				}
243 				goto bombout;
244 			    case 'T':
245 				if (attackee == nuke) {
246 				    if (rand_mod(2+scandist-prob) <
247 				      rand_mod(smarts/40+1))
248 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
249 				}
250 				if (thru_stars)
251 				    goto bombout;
252 				if (webnear && scandist-prob > 5) {
253 				    if (massacre || rand_mod(50) < super) {
254 					if (!torps && obj->energy > 1000) {
255 					    fire_phaser(obj, -dy, -dx);
256 					    while (!rand_mod(57-sm55))
257 						fire_phaser(obj, -dy, -dx);
258 					}
259 				    }
260 				}
261 				goto bombout;
262 			    case 'C': case 'c':
263 				if (thru_stars)
264 				    goto bombout;
265 				break;
266 			    case 'Q': case 'W': case 'Y': case 'U':
267 			    case 'I': case 'S': case 'D': case 'H': case 'J':
268 			    case 'L': case 'Z': case 'V': case 'M': case 'F':
269 				if (attackee == nuke) {
270 				    if (rand_mod(2+scandist-prob) <
271 				      rand_mod(smarts/40+1))
272 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
273 				    if (rand_mod(2))
274 					goto pot_shot;
275 				}
276 				if (madfriends > 1000) {
277 				    madfriends -= 200;
278 				    goto pot_shot;
279 				}
280 				/* FALL THROUGH */
281 			    case '+':
282 				if (attackee == nuke) {
283 				    if (smarts > 70) {
284 					if (
285 					 (obj->posx + obj->velx + XSIZE00)%XSIZE
286 					    == attackee->posx &&
287 					 (obj->posy + obj->vely + YSIZE00)%YSIZE
288 					    == attackee->posy ) {
289 					    tract(nuke,dy,dx,-1);
290 					}
291 					else
292 					    while (!rand_mod(82-sm80))
293 						tract(nuke,dy,dx,-1);
294 				    }
295 				    else if (smarts > 60 ||
296 				      rand_mod(2+scandist-prob) <
297 				      rand_mod(smarts/20+1))
298 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
299 				}
300 				torps = false;
301 				thru_stars = false;
302 				break;
303 			    case '|': case '-': case '/': case '\\':
304 				if (thru_stars)
305 				    goto bombout;
306 				webnear = (scandist-prob < 3);
307 				torps = false;
308 				break;
309 			    case 'x':
310 				if (attackee == nuke) {
311 				    if (rand_mod(2+scandist-prob) <
312 				      rand_mod(smarts/20+1))
313 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
314 				}
315 				if (thru_stars)
316 				    goto bombout;
317 				torps = true;
318 				break;
319 			    case 'o': case 'O': case '0':
320 				if (attackee == nuke) {
321 				    if (rand_mod(2+scandist-prob) <
322 				      rand_mod(smarts/20+1))
323 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
324 				}
325 				if (thru_stars)
326 				    goto bombout;
327 				torps = true;
328 				if (rand_mod(99+3*scandist) < smarts+3*prob) {
329 				    obj->vely = -dy + attackee->vely;
330 				    obj->velx = -dx + attackee->velx;
331 				    if (obj->flags & STATIC) {/* not a mover? */
332 					obj->flags &= ~STATIC;
333 					obj->prev->next = obj->next;
334 					obj->next->prev = obj->prev;
335 					root.prev->next = obj;
336 					obj->prev = root.prev;
337 					root.prev = obj;
338 					obj->next = &root;
339 				    }
340 				}
341 				if (obj->image != '0')
342 				    break;
343 			    /*FALLTHROUGH*/
344 			    case 'X':
345 				if (attackee == nuke) {
346 				    if (rand_mod(2+scandist-prob) <
347 				      rand_mod(smarts/20+1))
348 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
349 				}
350 				torps = true;
351 				if (thru_stars)
352 				    goto bombout;
353 				if (prob == scandist) {
354 				    int y, x;
355 
356 				    blast[y=(obj->posy+obj->vely+YSIZE00)%YSIZE]
357 					 [x=(obj->posx+obj->velx+XSIZE00)%XSIZE]
358 				      += (obj->image == '0' ? 2000 : 200);
359 				    yblasted[y] |= 1;
360 				    xblasted[x] |= 1;
361 				    blasted = true;
362 				}
363 				break;
364 			    case 'A':
365 				if (attackee != nuke) {
366 				    if (scandist-prob>1 && !rand_mod(51-super))
367 					tract(obj,-dy,-dx,1);
368 				}
369 				/* FALL THROUGH */
370 			    case '*': case '@':
371 				if (attackee == nuke) {
372 				    if (amb[cury][curx] != '~') {
373 					if (scandist-prob < nukedist) {
374 					    nukedist = scandist-prob;
375 					    nukey = dy;	/* nearest food in */
376 					    nukex = dx;	/*   this direction */
377 					}
378 					if (smarts > 55 && scandist-prob > 8) {
379 					    if (rand_mod(30+scandist-prob) <
380 					      rand_mod(smarts/20+1))
381 						tract(nuke,dy,dx,1);
382 					}
383 				    }
384 				    else if (obj->vely || obj->velx) {
385 					tract(nuke,dy,dx,1); /* for looks */
386 					obj->vely = obj->velx = 0;
387 				    }
388 				}
389 				if (!thru_stars) {
390 				    if (rand_mod(97-sm95))
391 					goto bombout;
392 				    else
393 					thru_stars = true;
394 				}
395 				break;
396 			    case '<': case '>':
397 				if (attackee == nuke) {
398 				    if ((!dy && scandist-prob < 8) ||
399 				      rand_mod(2+scandist-prob) <
400 				      rand_mod(smarts/20+1) ) {
401 					nuke->mass += 10000;
402 					tract(nuke,dy,dx,-1);
403 					nuke->mass -= 10000;
404 				    }
405 				}
406 				goto bombout;
407 			    case 'E': case 'B':
408 				if (attackee == nuke) {
409 				    if (rand_mod(2+scandist-prob) <
410 				      rand_mod(smarts/40+1))
411 					tract(nuke,dy,dx,rand_mod(3)?1:-1);
412 				}
413 				goto bombout;
414 			    default:
415 				goto bombout;
416 			    }
417 			}
418 			else {
419 			    if (thru_stars)
420 				goto bombout;
421 			}
422 		    }
423 bombout:            ; /* end of loop */
424 		}
425 	    }
426 	}
427 	if (attackee == nuke && nukedist < 100) {/* aim amoeba at nearest */
428 	    if (nukey < 0)			/*   free star */
429 		nukey = 2;
430 	    if (nukex < 0)
431 		nukex = 2;
432 	    nuke->strategy = nukey + (nukex << 2);
433 	}
434     }
435 }
436 
437 void
fire_phaser(OBJECT * obj,int dy,int dx)438 fire_phaser(OBJECT *obj, int dy, int dx)
439 {
440     int y;
441     int x;
442     int skipping;
443     int size=5000;
444     int decr = 50, oldy, oldx;
445     static char curchar[] = "@* ";
446 
447     if (obj == ent)
448 	decr = 100;
449     else if (obj == base) {
450 	decr = 1000;
451 	size = 200;
452     }
453     if (!dy)
454 	curchar[2] = '-';
455     else if (!dx)
456 	curchar[2] = '!';
457     else if (dy == dx)
458 	curchar[2] = '\\';
459     else
460 	curchar[2] = '/';
461     if (obj->energy >= decr) {
462 	obj->energy -= decr;
463 	for (
464 	  /* initialize */
465 	  skipping = (obj != base),
466 	  y = (obj->posy+(obj==base?dy*2:dy)+YSIZE00)%YSIZE,
467 	  x = (obj->posx+(obj==base?dx*2:dx)+XSIZE00)%XSIZE;
468 	  /* while */
469 	  size && (!occupant[y][x]||(skipping && occupant[y][x]->type==Star));
470 	  /* at end of loop */
471 	  y = (y+dy+YSIZE00) % YSIZE,
472 	  x = (x+dx+XSIZE00) % XSIZE,
473 	  size = size * 3 / 4 ) {
474 	    move(y+1,x*2,0);
475 	    beg_qwrite();
476 	    if (obj == base || obj->image == 'T') {
477 		*filler = '@';
478 		qwrite();
479 		*filler = '#';
480 		qwrite();
481 		*filler = '~';
482 		qwrite();
483 		*filler = '%';
484 		qwrite();
485 		*filler = ':';
486 		qwrite();
487 		*filler = '@';
488 	    }
489 	    else {
490 		*filler = size >= 500 ?
491 			  *curchar : (size >= 50 ?
492 				     curchar[1] :
493 				     curchar[2]);
494 	    }
495 	    qwrite();
496 	    if (occupant[y][x])
497 		qaddc(occupant[y][x]->image);
498 	    else {
499 		if (numamoebas)
500 		    qaddc(amb[y][x]);
501 		else
502 		    qaddspace();
503 		if (skipping)
504 		    skipping = 0;
505 	    }
506 	    end_qwrite();
507 	}
508 	if (size) {
509 	    char img;
510 
511 	    assert(occupant[y][x]);
512 	    img = occupant[y][x]->image;
513 	    if (occupant[y][x]->type == Crusher) {
514 		if (dy)
515 		    return;
516 	        if (dx==(img == '<' ? 1 : -1) ) {
517 		    occupant[y][x]->image =
518 			(occupant[y][x]->velx *= -1) < 0 ? '>' : '<';
519 		    return;
520 		}
521 	    }
522 	    else if (occupant[y][x]->flags & FRIENDLY)
523 		madfriends += 200;
524 	    if (numamoebas && amb[y][x] == '~' && smarts % 3 &&
525 		(smarts > 70 || rand_mod(smarts) > rand_mod(20)) ) {
526 		if (size > 10000)
527 		    modify_amoeba(y,x,1,'~',10);
528 		else if (size > 1000)
529 		    modify_amoeba(y,x,1,'~',7);
530 		else if (size > 50)
531 		    modify_amoeba(y,x,1,'~',5);
532 		else
533 		    modify_amoeba(y,x,1,'~',2);
534 		if (occupant[y][x] == nuke) {
535 		    nuke->strategy = rand_mod(30);
536 		    nuke->flags |= COUNTDOWN;
537 		}
538 		return;
539 	    }
540 	    else {
541 		move(y+1,x*2,0);
542 		beg_qwrite();
543 		if (img == ' ') {
544 		    *filler = occupant[y][x]->flags & PIRATE ? 'P' : 'R';
545 		    occupant[y][x]->image = *filler;
546 		    occupant[y][x]->strategy = 0;
547 		    qwrite();
548 		    qwrite();
549 		}
550 		else if (img == 'C' || img == 'c') {
551 		    cloaked = 0;
552 		    img += 2;
553 		    occupant[y][x]->image = img;
554 		    *filler = img;
555 		    qwrite();
556 		    qwrite();
557 		}
558 		else if (img == 'K' && size > 50)
559 		    occupant[y][x]->strategy = 0;
560 		*filler = '@';
561 		qwrite();
562 		*filler = '#';
563 		qwrite();
564 		*filler = '@';
565 		qwrite();
566 		*filler = '#';
567 		qwrite();
568 		*filler = '@';
569 		qwrite();
570 		qaddc(img);
571 		end_qwrite();
572 		oldy = y;
573 		oldx = x;
574 		y = (occupant[oldy][oldx]->posy + occupant[oldy][oldx]->vely +
575 			YSIZE00) % YSIZE;
576 		x = (occupant[oldy][oldx]->posx + occupant[oldy][oldx]->velx +
577 			XSIZE00) % XSIZE;
578 		if (occupant[y][x] && occupant[y][x]->type == Star) {
579 		    y = occupant[oldy][oldx]->posy;
580 		    x = occupant[oldy][oldx]->posx;
581 		}
582 		if (obj==base)
583 		    blast[y][x] += size>50 ? 15000 : (size>15 ? 1500 : 150);
584 		else if (obj==ent)
585 		    blast[y][x] += size*4;
586 		else if (obj->image=='T')
587 		    blast[y][x] += 15000;
588 		else
589 		    blast[y][x] += size*smarts/25;
590 		yblasted[y] |= 1;
591 		xblasted[x] |= 1;
592 		blasted = true;
593 	    }
594 	}
595     }
596 }
597 
598 int
tract(OBJECT * obj,int dy,int dx,int to_or_fro)599 tract(OBJECT *obj, int dy, int dx, int to_or_fro)
600 {
601     int y;
602     int x;
603     int size=10;
604     static char ch;
605     OBJECT *tractee;
606 
607     if (!dy)
608 	ch = '|';
609     else if (!dx)
610 	ch = '-';
611     else if (dy == dx)
612 	ch = '/';
613     else
614 	ch = '\\';
615     {
616 	for (
617 	  y = (obj->posy+dy+YSIZE00)%YSIZE,
618 	  x = (obj->posx+dx+XSIZE00)%XSIZE;
619 	  size && (!occupant[y][x]);
620 	  y = (y+dy+YSIZE00) % YSIZE, x = (x+dx+XSIZE00) % XSIZE, size--) {
621 	    move(y+1,x*2,0);
622 	    beg_qwrite();
623 	    *filler = ch;
624 	    qwrite();
625 	    qwrite();
626 	    if (numamoebas)
627 		qaddch(amb[y][x]);
628 	    else
629 		qaddspace();
630 	    end_qwrite();
631 	}
632 	tractee = occupant[y][x];
633 	if (size) {
634 	    assert(tractee);
635 	    if (numamoebas && obj != nuke && amb[y][x] == '~') {
636 		if (to_or_fro > 0)
637 		    modify_amoeba(y,x,2,'~',size);
638 		else
639 		    modify_amoeba(y,x,1,' ',size);
640 	    }
641 	    if (tractee->type != Web &&
642 		(tractee->mass < obj->mass * 5 ||
643 		 (tractee->type == Crusher && !dx) ) ) {
644 		if (tractee == ent) {
645 		    evely -= dy * to_or_fro;
646 		    evelx -= dx * to_or_fro;
647 		}
648 		else if (tractee == base) {
649 		    bvely -= dy * to_or_fro;
650 		    bvelx -= dx * to_or_fro;
651 		}
652 		else {
653 		    tractee->vely -= dy * to_or_fro;
654 		    tractee->velx -= dx * to_or_fro;
655 		}
656 		if (tractee->type == Torp ||
657 		    tractee->type == Star) {
658 		    if (tractee->flags & STATIC) {  /* not a mover? */
659 			tractee->flags &= ~STATIC;
660 			tractee->prev->next = tractee->next;
661 			tractee->next->prev = tractee->prev;
662 			root.prev->next = tractee;
663 			tractee->prev = root.prev;
664 			root.prev = tractee;
665 			tractee->next = &root;
666 		    }
667 		}
668 	    }
669 	    else if (tractee->type == Crusher && !dy &&
670 	      dx==(tractee->image == '<' ? 1 : -1) ) {
671 		setimage(tractee, (tractee->velx *= -1) < 0 ? '>' : '<');
672 	    }
673 	    if (tractee->mass * 5 > obj->mass)
674 		return(1);
675 	}
676     }
677     return(0);
678 }
679