1 /* SCCS Id: @(#)worm.c 3.4 1995/01/28 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
8 #define newseg() (struct wseg *) alloc(sizeof(struct wseg))
9 #define dealloc_seg(wseg) free((void *) (wseg))
11 /* worm segment structure */
14 xchar wx
, wy
; /* the segment's position */
17 STATIC_DCL
void toss_wsegs(struct wseg
*,BOOLEAN_P
);
18 STATIC_DCL
void shrink_worm(int);
19 STATIC_DCL
void random_dir(XCHAR_P
,XCHAR_P
,xchar
*,xchar
*);
20 STATIC_DCL
struct wseg
*create_worm_tail(int);
22 /* Description of long worm implementation.
24 * Each monst struct of the head of a tailed worm has a wormno set to
25 * 1 <= wormno < MAX_NUM_WORMS
26 * If wormno == 0 this does not mean that the monster is not a worm,
27 * it just means that the monster does not have a long worm tail.
29 * The actual segments of a worm are not full blown monst structs.
30 * They are small wseg structs, and their position in the levels.monsters[][]
31 * array is held by the monst struct of the head of the worm. This makes
32 * things like probing and hit point bookkeeping much easier.
34 * The segments of the long worms on a level are kept as an array of
35 * singly threaded linked lists. The wormno variable is used as an index
36 * for these segment arrays.
38 * wtails: The first (starting struct) of a linked list. This points
39 * to the tail (last) segment of the worm.
41 * wheads: The last (end) of a linked list of segments. This points to
42 * the segment that is at the same position as the real monster
43 * (the head). Note that the segment that wheads[wormno] points
44 * to, is not displayed. It is simply there to keep track of
45 * where the head came from, so that worm movement and display are
47 * Keeping the head segment of the worm at the end of the list
48 * of tail segments is an endless source of confusion, but it is
50 * From now on, we will use "start" and "end" to refer to the
51 * linked list and "head" and "tail" to refer to the worm.
53 * One final worm array is:
55 * wgrowtime: This tells us when to add another segment to the worm.
57 * When a worm is moved, we add a new segment at the head, and delete the
58 * segment at the tail (unless we want it to grow). This new head segment is
59 * located in the same square as the actual head of the worm. If we want
60 * to grow the worm, we don't delete the tail segment, and we give the worm
61 * extra hit points, which possibly go into its maximum.
63 * Non-moving worms (worm_nomove) are assumed to be surrounded by their own
64 * tail, and, thus, shrink instead of grow (as their tails keep going while
65 * their heads are stopped short). In this case, we delete the last tail
66 * segment, and remove hit points from the worm.
69 struct wseg
*wheads
[MAX_NUM_WORMS
] = DUMMY
, *wtails
[MAX_NUM_WORMS
] = DUMMY
;
70 long wgrowtime
[MAX_NUM_WORMS
] = DUMMY
;
75 * Find an unused worm tail slot and return the index. A zero means that
76 * there are no slots available. This means that the worm head can exist,
77 * it just cannot ever grow a tail.
79 * It, also, means that there is an optimisation to made. The [0] positions
80 * of the arrays are never used. Meaning, we really *could* have one more
81 * tailed worm on the level, or use a smaller array (using wormno - 1).
83 * Implementation is left to the interested hacker.
88 register int new_wormno
= 1;
90 while (new_wormno
< MAX_NUM_WORMS
) {
91 if (!wheads
[new_wormno
])
92 return new_wormno
; /* found an empty wtails[] slot at new_wormno */
96 return(0); /* level infested with worms */
102 * Use if (mon->wormno = get_wormno()) before calling this function!
104 * Initialize the worm entry. This will set up the worm grow time, and
105 * create and initialize the dummy segment for wheads[] and wtails[].
107 * If the worm has no tail (ie get_wormno() fails) then this function need
111 initworm(worm
, wseg_count
)
115 register struct wseg
*seg
, *new_tail
= create_worm_tail(wseg_count
);
116 register int wnum
= worm
->wormno
;
118 /* if (!wnum) return; bullet proofing */
121 wtails
[wnum
] = new_tail
;
122 for (seg
= new_tail
; seg
->nseg
; seg
= seg
->nseg
);
125 wtails
[wnum
] = wheads
[wnum
] = seg
= newseg();
126 seg
->nseg
= (struct wseg
*) 0;
130 wgrowtime
[wnum
] = 0L;
137 * Get rid of all worm segments on and following the given pointer curr.
138 * The display may or may not need to be updated as we free the segments.
142 toss_wsegs(curr
, display_update
)
143 register struct wseg
*curr
;
144 register boolean display_update
;
146 register struct wseg
*seg
;
151 /* remove from level.monsters[][] */
153 /* need to check curr->wx for genocided while migrating_mon */
155 remove_monster(curr
->wx
, curr
->wy
);
157 /* update screen before deallocation */
158 if (display_update
) newsym(curr
->wx
,curr
->wy
);
161 /* free memory used by the segment */
171 * Remove the tail segment of the worm (the starting segment of the list).
176 int wnum
; /* worm number */
180 if (wtails
[wnum
] == wheads
[wnum
]) return; /* no tail */
183 wtails
[wnum
] = seg
->nseg
;
184 seg
->nseg
= (struct wseg
*) 0;
185 toss_wsegs(seg
, TRUE
);
191 * Check for mon->wormno before calling this function!
193 * Move the worm. Maybe grow.
199 register struct wseg
*seg
, *new_seg
; /* new segment */
200 register int wnum
= worm
->wormno
; /* worm number */
203 /* if (!wnum) return; bullet proofing */
206 * Place a segment at the old worm head. The head has already moved.
209 place_worm_seg(worm
, seg
->wx
, seg
->wy
);
210 newsym(seg
->wx
,seg
->wy
); /* display the new segment */
213 * Create a new dummy segment head and place it at the end of the list.
216 new_seg
->wx
= worm
->mx
;
217 new_seg
->wy
= worm
->my
;
218 new_seg
->nseg
= (struct wseg
*) 0;
219 seg
->nseg
= new_seg
; /* attach it to the end of the list */
220 wheads
[wnum
] = new_seg
; /* move the end pointer */
223 if (wgrowtime
[wnum
] <= moves
) {
224 if (!wgrowtime
[wnum
])
225 wgrowtime
[wnum
] = moves
+ rnd(5);
227 wgrowtime
[wnum
] += rn1(15, 3);
229 if (worm
->mhp
> MHPMAX
) worm
->mhp
= MHPMAX
;
230 if (worm
->mhp
> worm
->mhpmax
) worm
->mhpmax
= worm
->mhp
;
232 /* The worm doesn't grow, so the last segment goes away. */
239 * Check for mon->wormno before calling this function!
241 * The worm don't move so it should shrink.
245 register struct monst
*worm
;
247 shrink_worm((int) worm
->wormno
); /* shrink */
250 worm
->mhp
-= 3; /* mhpmax not changed ! */
258 * Check for mon->wormno before calling this function!
264 register struct monst
*worm
;
266 register int wnum
= worm
->wormno
;
268 /* if (!wnum) return; bullet proofing */
272 /* This will also remove the real monster (ie 'w') from the its
273 * position in level.monsters[][].
275 toss_wsegs(wtails
[wnum
], TRUE
);
277 wheads
[wnum
] = wtails
[wnum
] = (struct wseg
*) 0;
283 * Check for mon->wormno before calling this function!
285 * If the hero is near any part of the worm, the worm will try to attack.
289 register struct monst
*worm
;
291 register int wnum
= worm
->wormno
;
292 register struct wseg
*seg
;
294 /* if (!wnum) return; bullet proofing */
296 /* This does not work right now because mattacku() thinks that the head is
297 * out of range of the player. We might try to kludge, and bring the head
298 * within range for a tiny moment, but this needs a bit more looking at
299 * before we decide to do this.
301 for (seg
= wtails
[wnum
]; seg
; seg
= seg
->nseg
)
302 if (distu(seg
->wx
, seg
->wy
) < 3)
303 (void) mattacku(worm
);
308 * Check for mon->wormno before calling this function!
310 * When hitting a worm (worm) at position x, y, with a weapon (weap),
311 * there is a chance that the worm will be cut in half, and a chance
312 * that both halves will survive.
314 * [ALI] Return true if worm is cut.
317 cutworm(worm
, x
, y
, weap
)
322 register struct wseg
*curr
, *new_tail
;
323 register struct monst
*new_worm
;
324 int wnum
= worm
->wormno
;
325 int cut_chance
, new_wnum
;
327 if (!wnum
) return 0; /* bullet proofing */
329 if (x
== worm
->mx
&& y
== worm
->my
) return 0; /* hit on head */
331 /* cutting goes best with a bladed weapon */
332 cut_chance
= rnd(20); /* Normally 1-16 does not cut */
333 /* Normally 17-20 does */
335 if (weap
&& is_blade(weap
)) /* With a blade 1- 6 does not cut */
336 cut_chance
+= 10; /* 7-20 does */
338 if (cut_chance
< 17) return 0; /* not good enough */
340 /* Find the segment that was attacked. */
343 while ( (curr
->wx
!= x
) || (curr
->wy
!= y
) ) {
346 impossible("cutworm: no segment at (%d,%d)", (int) x
, (int) y
);
351 /* If this is the tail segment, then the worm just loses it. */
352 if (curr
== wtails
[wnum
]) {
358 * Split the worm. The tail for the new worm is the old worm's tail.
359 * The tail for the old worm is the segment that follows "curr",
360 * and "curr" becomes the dummy segment under the new head.
362 new_tail
= wtails
[wnum
];
363 wtails
[wnum
] = curr
->nseg
;
364 curr
->nseg
= (struct wseg
*) 0; /* split the worm */
367 * At this point, the old worm is correct. Any new worm will have
368 * it's head at "curr" and its tail at "new_tail".
371 /* Sometimes the tail end dies. */
372 if (rn2(3) || !(new_wnum
= get_wormno())) {
373 if (flags
.mon_moving
)
374 pline("Part of the tail of %s is cut off.", mon_nam(worm
));
376 You("cut part of the tail off of %s.", mon_nam(worm
));
377 toss_wsegs(new_tail
, TRUE
);
378 if (worm
->mhp
> 1) worm
->mhp
/= 2;
382 remove_monster(x
, y
); /* clone_mon puts new head here */
383 new_worm
= clone_mon(worm
, x
, y
);
384 if (!new_worm
) { /* bugfix, I don't remember where I got it from --Amy */
387 new_worm
->wormno
= new_wnum
; /* affix new worm number */
389 /* Devalue the monster level of both halves of the worm. */
390 worm
->m_lev
= ((unsigned)worm
->m_lev
<= 3) ?
391 (unsigned)worm
->m_lev
: max((unsigned)worm
->m_lev
- 2, 3);
392 new_worm
->m_lev
= worm
->m_lev
;
394 /* Calculate the mhp on the new_worm for the (lower) monster level. */
395 new_worm
->mhpmax
= new_worm
->mhp
= d((int)new_worm
->m_lev
, 8);
397 /* Calculate the mhp on the old worm for the (lower) monster level. */
398 if (worm
->m_lev
> 3) {
399 worm
->mhpmax
= d((int)worm
->m_lev
, 8);
400 if (worm
->mhpmax
< worm
->mhp
) worm
->mhp
= worm
->mhpmax
;
403 wtails
[new_wnum
] = new_tail
; /* We've got all the info right now */
404 wheads
[new_wnum
] = curr
; /* so we can do this faster than */
405 wgrowtime
[new_wnum
] = 0L; /* trying to call initworm(). */
407 /* Place the new monster at all the segment locations. */
408 place_wsegs(new_worm
);
410 if (flags
.mon_moving
)
411 pline("%s is cut in half.", Monnam(worm
));
413 You("cut %s in half.", mon_nam(worm
));
422 * Refresh all of the segments of the given worm. This is only called
423 * from see_monster() in display.c or when a monster goes minvis. It
424 * is located here for modularity.
430 struct wseg
*curr
= wtails
[worm
->wormno
];
432 /* if (!mtmp->wormno) return; bullet proofing */
434 while (curr
!= wheads
[worm
->wormno
]) {
435 newsym(curr
->wx
,curr
->wy
);
443 * Display all of the segments of the given worm for detection.
446 detect_wsegs(worm
, use_detection_glyph
)
448 boolean use_detection_glyph
;
451 struct wseg
*curr
= wtails
[worm
->wormno
];
453 /* if (!mtmp->wormno) return; bullet proofing */
455 while (curr
!= wheads
[worm
->wormno
]) {
456 num
= use_detection_glyph
?
457 detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL
)) :
458 monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL
));
459 show_glyph(curr
->wx
,curr
->wy
,num
);
468 * Save the worm information for later use. The count is the number
469 * of segments, including the dummy. Called from save.c.
477 struct wseg
*curr
, *temp
;
479 if (perform_bwrite(mode
)) {
480 for (i
= 1; i
< MAX_NUM_WORMS
; i
++) {
481 for (count
= 0, curr
= wtails
[i
]; curr
; curr
= curr
->nseg
) count
++;
482 /* Save number of segments */
483 bwrite(fd
, (void *) &count
, sizeof(int));
484 /* Save segment locations of the monster. */
486 for (curr
= wtails
[i
]; curr
; curr
= curr
->nseg
) {
487 bwrite(fd
, (void *) &(curr
->wx
), sizeof(xchar
));
488 bwrite(fd
, (void *) &(curr
->wy
), sizeof(xchar
));
492 bwrite(fd
, (void *) wgrowtime
, sizeof(wgrowtime
));
495 if (release_data(mode
)) {
496 /* Free the segments only. savemonchn() will take care of the
498 for (i
= 1; i
< MAX_NUM_WORMS
; i
++) {
499 if (!(curr
= wtails
[i
])) continue;
503 dealloc_seg(curr
); /* free the segment */
506 wheads
[i
] = wtails
[i
] = (struct wseg
*) 0;
515 * Restore the worm information from the save file. Called from restore.c
522 struct wseg
*curr
, *temp
;
524 for (i
= 1; i
< MAX_NUM_WORMS
; i
++) {
525 mread(fd
, (void *) &count
, sizeof(int));
526 if (!count
) continue; /* none */
528 /* Get the segments. */
529 for (curr
= (struct wseg
*) 0, j
= 0; j
< count
; j
++) {
531 temp
->nseg
= (struct wseg
*) 0;
532 mread(fd
, (void *) &(temp
->wx
), sizeof(xchar
));
533 mread(fd
, (void *) &(temp
->wy
), sizeof(xchar
));
542 mread(fd
, (void *) wgrowtime
, sizeof(wgrowtime
));
548 * Place the segments of the given worm. Called from restore.c
554 struct wseg
*curr
= wtails
[worm
->wormno
];
556 /* if (!mtmp->wormno) return; bullet proofing */
558 while (curr
!= wheads
[worm
->wormno
]) {
559 place_worm_seg(worm
,curr
->wx
,curr
->wy
);
567 * This function is equivalent to the remove_monster #define in
568 * rm.h, only it will take the worm *and* tail out of the levels array.
569 * It does not get rid of (dealloc) the worm tail structures, and it does
570 * not remove the mon from the fmon chain.
574 register struct monst
*worm
;
576 register struct wseg
*curr
= wtails
[worm
->wormno
];
578 /* if (!mtmp->wormno) return; bullet proofing */
581 remove_monster(curr
->wx
, curr
->wy
);
582 newsym(curr
->wx
, curr
->wy
);
588 * place_worm_tail_randomly()
590 * Place a worm tail somewhere on a level behind the head.
591 * This routine essentially reverses the order of the wsegs from head
592 * to tail while placing them.
593 * x, and y are most likely the worm->mx, and worm->my, but don't *need* to
594 * be, if somehow the head is disjoint from the tail.
597 place_worm_tail_randomly(worm
, x
, y
)
601 int wnum
= worm
->wormno
;
602 struct wseg
*curr
= wtails
[wnum
];
603 struct wseg
*new_tail
;
604 register xchar ox
= x
, oy
= y
;
606 /* if (!wnum) return; bullet proofing */
608 if (wnum
&& (!wtails
[wnum
] || !wheads
[wnum
]) ) {
609 impossible("place_worm_tail_randomly: wormno is set without a tail!");
613 wheads
[wnum
] = new_tail
= curr
;
615 new_tail
->nseg
= (struct wseg
*) 0;
623 /* pick a random direction from x, y and search for goodpos() */
626 random_dir(ox
, oy
, &nx
, &ny
);
627 } while (!goodpos(nx
, ny
, worm
, 0) && (tryct
++ < 50));
630 place_worm_seg(worm
, nx
, ny
);
635 wtails
[wnum
]->nseg
= new_tail
;
636 new_tail
= wtails
[wnum
];
638 } else { /* Oops. Truncate because there was */
639 toss_wsegs(curr
, FALSE
); /* no place for the rest of it */
640 curr
= (struct wseg
*) 0;
646 * Given a coordinate x, y.
647 * return in *nx, *ny, the coordinates of one of the <= 8 squares adjoining.
649 * This function, and the loop it serves, could be eliminated by coding
650 * enexto() with a search radius.
654 random_dir(x
, y
, nx
, ny
)
656 register xchar
*nx
, *ny
;
661 *nx
+= (x
> 1 ? /* extreme left ? */
662 (x
< COLNO
? /* extreme right ? */
663 (rn2(3) - 1) /* neither so +1, 0, or -1 */
664 : -rn2(2)) /* 0, or -1 */
665 : rn2(2)); /* 0, or 1 */
667 *ny
+= (*nx
== x
? /* same kind of thing with y */
685 * the number of visible segments that a worm has.
693 register struct wseg
*curr
= (wtails
[mtmp
->wormno
])->nseg
;
695 /* if (!mtmp->wormno) return 0; bullet proofing */
705 /* create_worm_tail()
707 * will create a worm tail chain of (num_segs + 1) and return a pointer to it.
711 create_worm_tail(num_segs
)
715 register struct wseg
*new_tail
, *curr
;
717 if (!num_segs
) return (struct wseg
*)0;
719 new_tail
= curr
= newseg();
720 curr
->nseg
= (struct wseg
*)0;
724 while (i
< num_segs
) {
725 curr
->nseg
= newseg();
727 curr
->nseg
= (struct wseg
*)0;
738 * Is any segment of this worm in viewing range? Note: caller must check
739 * invisibility and telepathy (which should only show the head anyway).
740 * Mostly used in the canseemon() macro.
746 struct wseg
*curr
= wtails
[worm
->wormno
];
749 if(cansee(curr
->wx
,curr
->wy
)) return TRUE
;