sphere.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /* =============================================================================
  2. * Program: ularn
  3. * FILENAME: sphere.c
  4. *
  5. * DESCRIPTION:
  6. * This module contains functions for maintaining & moving the spheres of
  7. * annihilation.
  8. *
  9. * =============================================================================
  10. * EXPORTED VARIABLES
  11. *
  12. * None
  13. *
  14. * =============================================================================
  15. * EXPORTED FUNCTIONS
  16. *
  17. * rmsphere : Remove a sphere from a location on the map
  18. * newsphere : Create a new sphere
  19. * movsphere : Move a sphere
  20. * free_spheres : Free all allocated spheres
  21. * write_spheres : Write the spheres to the save file
  22. * read_spheres : Read the spheres from the save file
  23. *
  24. * =============================================================================
  25. */
  26. #include "sphere.h"
  27. #include "header.h"
  28. #include "itm.h"
  29. #include "monster.h"
  30. #include "player.h"
  31. #include "saveutils.h"
  32. #include "ularn_game.h"
  33. #include "ularn_win.h"
  34. /* =============================================================================
  35. * Local variables
  36. */
  37. struct sphere {
  38. struct sphere *next; /* pointer to next structure */
  39. short x, y, lev; /* location of the sphere */
  40. short dir; /* direction sphere is going in */
  41. short lifetime; /* duration of the sphere */
  42. };
  43. /*
  44. * The start of the list of spheres
  45. */
  46. static struct sphere *spheres = NULL;
  47. /*
  48. * The next sphere to process when processing the movement for list of spheres.
  49. * NOTE:
  50. * This may be changed by rmsphere deleting an unprocessed sphere when
  51. * two spheres collide.
  52. */
  53. static struct sphere *sp2;
  54. /* =============================================================================
  55. * Local functions
  56. */
  57. /* =============================================================================
  58. * FUNCTION: sphboom
  59. *
  60. * DESCRIPTION:
  61. * Function to perform the effects of a sphere detonation.
  62. *
  63. * PARAMETERS:
  64. *
  65. * x : The x coordinate of the blast
  66. *
  67. * y : The y coordinate of the blast
  68. *
  69. * RETURN VALUE:
  70. *
  71. * None.
  72. */
  73. static void sphboom(int x, int y) {
  74. int have_talisman;
  75. int i;
  76. int j;
  77. int xl, xh, yl, yh;
  78. /* check if the player has the talisman of the sphere */
  79. have_talisman = player_has_item(OSPHTALISMAN);
  80. /* sphere explosion ends hold monster and cancellation */
  81. if (c[HOLDMONST])
  82. c[HOLDMONST] = 1;
  83. if (c[CANCELLATION])
  84. c[CANCELLATION] = 1;
  85. xl = x - 2;
  86. yl = y - 2;
  87. xh = x + 3;
  88. yh = y + 3;
  89. if (level == 0) {
  90. /* On the home level the boom can go right to the edge of the map */
  91. if (xl < 0)
  92. xl = 0;
  93. if (xh > MAXX)
  94. xh = MAXX;
  95. if (yl < 0)
  96. yl = 0;
  97. if (yh > MAXY)
  98. yh = MAXY;
  99. } else {
  100. /* In the dungeon the boom stops at the edge wall */
  101. if (xl < 1)
  102. xl = 1;
  103. if (xh > (MAXX - 1))
  104. xh = MAXX - 1;
  105. if (yl < 1)
  106. yl = 1;
  107. if (yh > (MAXY - 1))
  108. yh = MAXY - 1;
  109. }
  110. /* wipe out everything in the range of the sphere */
  111. for (j = xl; j < xh; j++) {
  112. for (i = yl; i < yh; i++) {
  113. item[j][i] = ONOTHING;
  114. if (!mon_has_item(j, i, OSPHTALISMAN))
  115. /* The monster was caught in the explosion */
  116. mitem[j][i].mon = MONST_NONE;
  117. else {
  118. /* The monster has the talisman, and is unaffected */
  119. Printf("\nThe %s is unaffected by the blast!",
  120. monster[(int)mitem[j][i].mon].name);
  121. }
  122. show1cell(j, i);
  123. if ((!have_talisman) && (playerx == j) && (playery == i)) {
  124. /* player killed in explosion */
  125. UlarnBeep();
  126. Print("\nYou were too close to the sphere!");
  127. nap(3000);
  128. died(DIED_SPHERE_ANNIHILATION, 0);
  129. }
  130. }
  131. }
  132. /* Analyse wall connections and redisplay */
  133. AnalyseWalls(xl - 1, yl - 1, xh, yh);
  134. for (i = xl - 1; i < xh + 1; i++) {
  135. for (j = yl - 1; j < yh + 1; j++) {
  136. if (checkxy(i, j))
  137. if ((know[i][j] != OUNKNOWN) && (item[i][j] == OWALL))
  138. show1cell(i, j);
  139. }
  140. }
  141. }
  142. /* =============================================================================
  143. * FUNCTION: msphere
  144. *
  145. * DESCRIPTION:
  146. * Move 1 sphere of annihiliation.
  147. *
  148. * PARAMETERS:
  149. *
  150. * sp : pointer to the sphere to be moved
  151. *
  152. * create : flag indicating if this is the first creation of the sphere
  153. * for creation, don't move the sphere, just place it at the curren
  154. * position.
  155. *
  156. * RETURN VALUE:
  157. *
  158. * 1 if the sphere moved,
  159. * 0 if the sphere is removed
  160. */
  161. int msphere(struct sphere *sp, int create) {
  162. int x, y;
  163. int i, j;
  164. MonsterIdType m;
  165. int it;
  166. int have_talisman;
  167. int mon_has_talisman;
  168. if (level != sp->lev)
  169. /* Not on the correct level for the sphere, so return */
  170. return 1;
  171. /* no movement if direction not found */
  172. if (sp->dir >= 9)
  173. sp->dir = 0;
  174. x = sp->x;
  175. y = sp->y;
  176. if (!create) {
  177. /*
  178. * The sphere was not just created, so clear the current location
  179. * and update the sphere position.
  180. */
  181. item[x][y] = ONOTHING;
  182. know[x][y] = item[x][y];
  183. /* show the now moved sphere */
  184. show1cell(x, y);
  185. x += diroffx[sp->dir];
  186. y += diroffy[sp->dir];
  187. }
  188. /* don't go out of bounds */
  189. if (level == 0) {
  190. /* on the home level the entire map area is valid */
  191. vxy(x, y);
  192. } else {
  193. /*
  194. * On dungeon levels don't move onto the edge walls surrounding the
  195. * dungeon.
  196. */
  197. if (x < 1)
  198. x = 1;
  199. if (x >= MAXX - 1)
  200. x = MAXX - 2;
  201. if (y < 1)
  202. y = 1;
  203. if (y >= MAXY - 1)
  204. y = MAXY - 2;
  205. }
  206. /*
  207. * store the new sphere position back in the sphere record
  208. */
  209. sp->x = (short)x;
  210. sp->y = (short)y;
  211. /* check if the player has the talisman of the sphere */
  212. have_talisman = player_has_item(OSPHTALISMAN);
  213. m = mitem[x][y].mon;
  214. it = item[x][y];
  215. if (m != MONST_NONE)
  216. mon_has_talisman = mon_has_item(x, y, OSPHTALISMAN);
  217. else
  218. mon_has_talisman = 0;
  219. /* demons dispel spheres if the player doesn't have the talisman */
  220. if ((!have_talisman) && (m >= DEMONLORD)) {
  221. know[x][y] = item[x][y];
  222. /* show the demon (ha ha) */
  223. show1cell(x, y);
  224. Printf("\nThe %s dispels the sphere!", monster[m].name);
  225. UlarnBeep();
  226. /* remove any spheres that are here */
  227. rmsphere(x, y);
  228. return 0;
  229. }
  230. /* disenchantress cancels spheres if the player doesn't have the talisman */
  231. if ((!have_talisman) && (m == DISENCHANTRESS)) {
  232. Printf("\nThe %s cancels the sphere!", monster[m].name);
  233. UlarnBeep();
  234. sphboom(x, y); /* blow up stuff around sphere */
  235. rmsphere(x, y); /* remove any spheres that are here */
  236. return 0;
  237. }
  238. /* cancellation cancels spheres if the player doesn't have the talisman */
  239. if ((!have_talisman) && c[CANCELLATION]) {
  240. Print("\nAs the cancellation takes effect, you hear a great earth shaking "
  241. "blast!");
  242. UlarnBeep();
  243. sphboom(x, y); /* blow up stuff around sphere */
  244. rmsphere(x, y); /* remove any spheres that are here */
  245. return 0;
  246. }
  247. /*
  248. * Collision of sphere and player is not good if the player doesn't have the
  249. * talisman
  250. */
  251. if ((!have_talisman) && (playerx == x) && (playery == y)) {
  252. Print("\nYou have been enveloped by the zone of nothingness!\n");
  253. UlarnBeep();
  254. rmsphere(x, y); /* remove any spheres that are here */
  255. nap(4000);
  256. died(DIED_SELF_ANNIHLATED, 0);
  257. return 0;
  258. }
  259. /* collision of spheres detonates spheres */
  260. if (item[x][y] == OANNIHILATION) {
  261. Print("\nTwo spheres of annihilation collide! You hear a great "
  262. "earth-shaking blast!");
  263. UlarnBeep();
  264. rmsphere(x, y);
  265. sphboom(x, y); /* blow up stuff around sphere */
  266. rmsphere(x, y); /* remove the other sphere */
  267. return 0;
  268. }
  269. /* The sphere still exists, so put it on the map in the new position */
  270. item[x][y] = OANNIHILATION;
  271. if (it == OWALL) {
  272. /* Destroyed a wall, so analyse wall connections and redisplay */
  273. AnalyseWalls(x - 1, y - 1, x + 1, y + 1);
  274. for (i = x - 1; i <= x + 1; i++) {
  275. for (j = y - 1; j <= y + 1; j++) {
  276. if (checkxy(i, j))
  277. if ((know[i][j] != OUNKNOWN) && (item[i][j] == OWALL))
  278. show1cell(i, j);
  279. }
  280. }
  281. }
  282. if (mon_has_talisman)
  283. Printf("\nThe %s is unaffected by the sphere of annihilation!",
  284. monster[m].name);
  285. else
  286. mitem[x][y].mon = MONST_NONE;
  287. know[x][y] = item[x][y];
  288. show1cell(x, y);
  289. return 1;
  290. }
  291. /* =============================================================================
  292. * Exported functions
  293. */
  294. /* =============================================================================
  295. * FUNCTION: rmsphere
  296. */
  297. void rmsphere(int x, int y) {
  298. struct sphere *sp;
  299. struct sphere *prev_sp;
  300. struct sphere *tmp;
  301. sp = spheres;
  302. prev_sp = NULL;
  303. while (sp != NULL) {
  304. if ((level == sp->lev) && (x == sp->x) && (y == sp->y)) {
  305. /*
  306. * The sphere is on this level and at the location of the sphere to be
  307. * deleted
  308. */
  309. item[x][y] = ONOTHING;
  310. know[x][y] = item[x][y];
  311. /* show the now missing sphere */
  312. show1cell(x, y);
  313. c[SPHCAST]--;
  314. tmp = sp; /* store the ponter to the sphere to be deleted */
  315. sp = sp->next; /* get a pointer to the next sphere */
  316. if (tmp == spheres)
  317. /* remove from the head of the list */
  318. spheres = sp;
  319. if (prev_sp != NULL)
  320. /* link over the sphere to be deleted */
  321. prev_sp->next = sp;
  322. if (tmp == sp2)
  323. /*
  324. * The sphere being deleted is the next sphere to move, so
  325. * update the next sphere to move
  326. */
  327. sp2 = sp;
  328. free((char *)tmp);
  329. /* Found the sphere to be removed, so return */
  330. return;
  331. } else {
  332. /* advance to the next sphere */
  333. prev_sp = sp;
  334. sp = sp->next;
  335. }
  336. }
  337. }
  338. /* =============================================================================
  339. * FUNCTION: newsphere
  340. */
  341. void newsphere(int x, int y, int dir, int life) {
  342. struct sphere *sp;
  343. /* Allocate the new sphere */
  344. sp = (struct sphere *)malloc(sizeof(struct sphere));
  345. if (sp == (struct sphere *)NULL)
  346. died(DIED_INTERNAL_COMPLICATIONS, 0);
  347. sp->x = (short)x;
  348. sp->y = (short)y;
  349. sp->lev = (short)level;
  350. sp->dir = (short)dir;
  351. sp->lifetime = (short)life;
  352. sp->next = spheres;
  353. spheres = sp;
  354. c[SPHCAST]++;
  355. msphere(sp, 1);
  356. /*
  357. * The new sphere is about to move in movemonst , so give the player
  358. * a chance to see it in the first position it is cast.
  359. */
  360. nap(500);
  361. }
  362. /* =============================================================================
  363. * FUNCTION: movsphere
  364. */
  365. void movsphere(void) {
  366. struct sphere *sp;
  367. sp = spheres;
  368. /* look through sphere list */
  369. while (sp != NULL) {
  370. /*
  371. * Set the next sphere to process
  372. * Note: This may be changed by msphere if two spheres collide.
  373. */
  374. sp2 = sp->next;
  375. /* has sphere run out of gas? */
  376. if (sp->lev == level) {
  377. /*
  378. * Only move spheres on this level.
  379. * All other spheres are in stasis.
  380. */
  381. sp->lifetime--;
  382. if (sp->lifetime < 0)
  383. /* delete sphere */
  384. rmsphere(sp->x, sp->y);
  385. else {
  386. /* time to move the sphere */
  387. if (rnd((int)max(7, c[INTELLIGENCE] >> 1)) <= 2)
  388. /* sphere changes direction */
  389. sp->dir = (short)rnd(8);
  390. msphere(sp, 0);
  391. }
  392. }
  393. /*
  394. * advance to the next sphere
  395. * Do it this way as the moved sphere may have been deleted, and we don't
  396. * want to access through unallocated memory do we.
  397. */
  398. sp = sp2;
  399. }
  400. }
  401. /* =============================================================================
  402. * FUNCTION: free_spheres
  403. */
  404. void free_spheres(void) {
  405. struct sphere *sp;
  406. struct sphere *next_sp;
  407. sp = spheres;
  408. /* look through sphere list */
  409. while (sp != NULL) {
  410. next_sp = sp->next;
  411. free((char *)sp);
  412. sp = next_sp;
  413. }
  414. spheres = NULL;
  415. }
  416. /* =============================================================================
  417. * FUNCTION: write_spheres
  418. */
  419. void write_spheres(FILE *fp) {
  420. int count;
  421. struct sphere *sp;
  422. /* count the number of spheres */
  423. sp = spheres;
  424. count = 0;
  425. while (sp != NULL) {
  426. count++;
  427. sp = sp->next;
  428. }
  429. /* write the count */
  430. bwrite(fp, (char *)&count, sizeof(int));
  431. sp = spheres;
  432. while (sp != NULL) {
  433. bwrite(fp, (char *)sp, sizeof(struct sphere));
  434. sp = sp->next;
  435. }
  436. }
  437. /* =============================================================================
  438. * FUNCTION: read_spheres
  439. */
  440. void read_spheres(FILE *fp) {
  441. int count;
  442. struct sphere *sp;
  443. spheres = NULL;
  444. bread(fp, (char *)&count, sizeof(int));
  445. while (count > 0) {
  446. sp = (struct sphere *)malloc(sizeof(struct sphere));
  447. bread(fp, (char *)sp, sizeof(struct sphere));
  448. sp->next = spheres;
  449. spheres = sp;
  450. count--;
  451. }
  452. }