Below is the full text to src/light.c from NetHack 3.4.3. To link to a particular line, write [[light.c#line123]], for example.
Top of file[]
1. /* SCCS Id: @(#)light.c 3.4 1997/04/10 */ 2. /* Copyright (c) Dean Luick, 1994 */ 3. /* NetHack may be freely redistributed. See license for details. */ 4.
| The NetHack General Public License applies to screenshots, source code and other content from NetHack. |
5. #include "hack.h" 6. #include "lev.h" /* for checking save modes */ 7. 8. /* 9. * Mobile light sources. 10. * 11. * This implementation minimizes memory at the expense of extra 12. * recalculations. 13. * 14. * Light sources are "things" that have a physical position and range. 15. * They have a type, which gives us information about them. Currently 16. * they are only attached to objects and monsters. Note well: the 17. * polymorphed-player handling assumes that both youmonst.m_id and 18. * youmonst.mx will always remain 0. 19. * 20. * Light sources, like timers, either follow game play (RANGE_GLOBAL) or 21. * stay on a level (RANGE_LEVEL). Light sources are unique by their 22. * (type, id) pair. For light sources attached to objects, this id 23. * is a pointer to the object. 24. * 25. * The major working function is do_light_sources(). It is called 26. * when the vision system is recreating its "could see" array. Here 27. * we add a flag (TEMP_LIT) to the array for all locations that are lit 28. * via a light source. The bad part of this is that we have to 29. * re-calculate the LOS of each light source every time the vision 30. * system runs. Even if the light sources and any topology (vision blocking 31. * positions) have not changed. The good part is that no extra memory 32. * is used, plus we don't have to figure out how far the sources have moved, 33. * or if the topology has changed. 34. * 35. * The structure of the save/restore mechanism is amazingly similar to 36. * the timer save/restore. This is because they both have the same 37. * principals of having pointers into objects that must be recalculated 38. * across saves and restores. 39. */ 40. 41. #ifdef OVL3 42. 43. /* flags */ 44. #define LSF_SHOW 0x1 /* display the light source */ 45. #define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ 46. 47. static light_source *light_base = 0; 48. 49. STATIC_DCL void FDECL(write_ls, (int, light_source *)); 50. STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P)); 51. 52. /* imported from vision.c, for small circles */ 53. extern char circle_data[]; 54. extern char circle_start[]; 55. 56.
new_light_source[]
57. /* Create a new light source. */ 58. void 59. new_light_source(x, y, range, type, id) 60. xchar x, y; 61. int range, type; 62. genericptr_t id; 63. { 64. light_source *ls; 65. 66. if (range > MAX_RADIUS || range < 1) { 67. impossible("new_light_source: illegal range %d", range); 68. return; 69. } 70. 71. ls = (light_source *) alloc(sizeof(light_source)); 72. 73. ls->next = light_base; 74. ls->x = x; 75. ls->y = y; 76. ls->range = range; 77. ls->type = type; 78. ls->id = id; 79. ls->flags = 0; 80. light_base = ls; 81. 82. vision_full_recalc = 1; /* make the source show up */ 83. } 84.
del_light_source[]
85. /* 86. * Delete a light source. This assumes only one light source is attached 87. * to an object at a time. 88. */ 89. void 90. del_light_source(type, id) 91. int type; 92. genericptr_t id; 93. { 94. light_source *curr, *prev; 95. genericptr_t tmp_id; 96. 97. /* need to be prepared for dealing a with light source which 98. has only been partially restored during a level change 99. (in particular: chameleon vs prot. from shape changers) */ 100. switch (type) { 101. case LS_OBJECT: tmp_id = (genericptr_t)(((struct obj *)id)->o_id); 102. break; 103. case LS_MONSTER: tmp_id = (genericptr_t)(((struct monst *)id)->m_id); 104. break; 105. default: tmp_id = 0; 106. break; 107. } 108. 109. for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) { 110. if (curr->type != type) continue; 111. if (curr->id == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id : id)) { 112. if (prev) 113. prev->next = curr->next; 114. else 115. light_base = curr->next; 116. 117. free((genericptr_t)curr); 118. vision_full_recalc = 1; 119. return; 120. } 121. } 122. impossible("del_light_source: not found type=%d, id=0x%lx", type, (long)id); 123. } 124.
do_light_sources[]
125. /* Mark locations that are temporarily lit via mobile light sources. */ 126. void 127. do_light_sources(cs_rows) 128. char **cs_rows; 129. { 130. int x, y, min_x, max_x, max_y, offset; 131. char *limits; 132. short at_hero_range = 0; 133. light_source *ls; 134. char *row; 135. 136. for (ls = light_base; ls; ls = ls->next) { 137. ls->flags &= ~LSF_SHOW; 138. 139. /* 140. * Check for moved light sources. It may be possible to 141. * save some effort if an object has not moved, but not in 142. * the current setup -- we need to recalculate for every 143. * vision recalc. 144. */ 145. if (ls->type == LS_OBJECT) { 146. if (get_obj_location((struct obj *) ls->id, &ls->x, &ls->y, 0)) 147. ls->flags |= LSF_SHOW; 148. } else if (ls->type == LS_MONSTER) { 149. if (get_mon_location((struct monst *) ls->id, &ls->x, &ls->y, 0)) 150. ls->flags |= LSF_SHOW; 151. } 152. 153. /* minor optimization: don't bother with duplicate light sources */ 154. /* at hero */ 155. if (ls->x == u.ux && ls->y == u.uy) { 156. if (at_hero_range >= ls->range) 157. ls->flags &= ~LSF_SHOW; 158. else 159. at_hero_range = ls->range; 160. } 161. 162. if (ls->flags & LSF_SHOW) { 163. /* 164. * Walk the points in the circle and see if they are 165. * visible from the center. If so, mark'em. 166. * 167. * Kevin's tests indicated that doing this brute-force 168. * method is faster for radius <= 3 (or so). 169. */ 170. limits = circle_ptr(ls->range); 171. if ((max_y = (ls->y + ls->range)) >= ROWNO) max_y = ROWNO-1; 172. if ((y = (ls->y - ls->range)) < 0) y = 0; 173. for (; y <= max_y; y++) { 174. row = cs_rows[y]; 175. offset = limits[abs(y - ls->y)]; 176. if ((min_x = (ls->x - offset)) < 0) min_x = 0; 177. if ((max_x = (ls->x + offset)) >= COLNO) max_x = COLNO-1; 178. 179. if (ls->x == u.ux && ls->y == u.uy) { 180. /* 181. * If the light source is located at the hero, then 182. * we can use the COULD_SEE bits already calcualted 183. * by the vision system. More importantly than 184. * this optimization, is that it allows the vision 185. * system to correct problems with clear_path(). 186. * The function clear_path() is a simple LOS 187. * path checker that doesn't go out of its way 188. * make things look "correct". The vision system 189. * does this. 190. */ 191. for (x = min_x; x <= max_x; x++) 192. if (row[x] & COULD_SEE) 193. row[x] |= TEMP_LIT; 194. } else { 195. for (x = min_x; x <= max_x; x++) 196. if ((ls->x == x && ls->y == y) 197. || clear_path((int)ls->x, (int) ls->y, x, y)) 198. row[x] |= TEMP_LIT; 199. } 200. } 201. } 202. } 203. } 204.
find_mid[]
205. /* (mon->mx == 0) implies migrating */ 206. #define mon_is_local(mon) ((mon)->mx > 0) 207. 208. struct monst * 209. find_mid(nid, fmflags) 210. unsigned nid; 211. unsigned fmflags; 212. { 213. struct monst *mtmp; 214. 215. if (!nid) 216. return &youmonst; 217. if (fmflags & FM_FMON) 218. for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) 219. if (!DEADMONSTER(mtmp) && mtmp->m_id == nid) return mtmp; 220. if (fmflags & FM_MIGRATE) 221. for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) 222. if (mtmp->m_id == nid) return mtmp; 223. if (fmflags & FM_MYDOGS) 224. for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon) 225. if (mtmp->m_id == nid) return mtmp; 226. return (struct monst *) 0; 227. } 228.
save_light_sources[]
229. /* Save all light sources of the given range. */ 230. void 231. save_light_sources(fd, mode, range) 232. int fd, mode, range; 233. { 234. int count, actual, is_global; 235. light_source **prev, *curr; 236. 237. if (perform_bwrite(mode)) { 238. count = maybe_write_ls(fd, range, FALSE); 239. bwrite(fd, (genericptr_t) &count, sizeof count); 240. actual = maybe_write_ls(fd, range, TRUE); 241. if (actual != count) 242. panic("counted %d light sources, wrote %d! [range=%d]", 243. count, actual, range); 244. } 245. 246. if (release_data(mode)) { 247. for (prev = &light_base; (curr = *prev) != 0; ) { 248. if (!curr->id) { 249. impossible("save_light_sources: no id! [range=%d]", range); 250. is_global = 0; 251. } else 252. switch (curr->type) { 253. case LS_OBJECT: 254. is_global = !obj_is_local((struct obj *)curr->id); 255. break; 256. case LS_MONSTER: 257. is_global = !mon_is_local((struct monst *)curr->id); 258. break; 259. default: 260. is_global = 0; 261. impossible("save_light_sources: bad type (%d) [range=%d]", 262. curr->type, range); 263. break; 264. } 265. /* if global and not doing local, or vice versa, remove it */ 266. if (is_global ^ (range == RANGE_LEVEL)) { 267. *prev = curr->next; 268. free((genericptr_t)curr); 269. } else { 270. prev = &(*prev)->next; 271. } 272. } 273. } 274. } 275.
restore_light_sources[]
276. /* 277. * Pull in the structures from disk, but don't recalculate the object 278. * pointers. 279. */ 280. void 281. restore_light_sources(fd) 282. int fd; 283. { 284. int count; 285. light_source *ls; 286. 287. /* restore elements */ 288. mread(fd, (genericptr_t) &count, sizeof count); 289. 290. while (count-- > 0) { 291. ls = (light_source *) alloc(sizeof(light_source)); 292. mread(fd, (genericptr_t) ls, sizeof(light_source)); 293. ls->next = light_base; 294. light_base = ls; 295. } 296. } 297.
relink_light_sources[]
298. /* Relink all lights that are so marked. */ 299. void 300. relink_light_sources(ghostly) 301. boolean ghostly; 302. { 303. char which; 304. unsigned nid; 305. light_source *ls; 306. 307. for (ls = light_base; ls; ls = ls->next) { 308. if (ls->flags & LSF_NEEDS_FIXUP) { 309. if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { 310. if (ghostly) { 311. if (!lookup_id_mapping((unsigned)ls->id, &nid)) 312. impossible("relink_light_sources: no id mapping"); 313. } else 314. nid = (unsigned) ls->id; 315. if (ls->type == LS_OBJECT) { 316. which = 'o'; 317. ls->id = (genericptr_t) find_oid(nid); 318. } else { 319. which = 'm'; 320. ls->id = (genericptr_t) find_mid(nid, FM_EVERYWHERE); 321. } 322. if (!ls->id) 323. impossible("relink_light_sources: cant find %c_id %d", 324. which, nid); 325. } else 326. impossible("relink_light_sources: bad type (%d)", ls->type); 327. 328. ls->flags &= ~LSF_NEEDS_FIXUP; 329. } 330. } 331. } 332.
maybe_write_ls[]
333. /* 334. * Part of the light source save routine. Count up the number of light 335. * sources that would be written. If write_it is true, actually write 336. * the light source out. 337. */ 338. STATIC_OVL int 339. maybe_write_ls(fd, range, write_it) 340. int fd, range; 341. boolean write_it; 342. { 343. int count = 0, is_global; 344. light_source *ls; 345. 346. for (ls = light_base; ls; ls = ls->next) { 347. if (!ls->id) { 348. impossible("maybe_write_ls: no id! [range=%d]", range); 349. continue; 350. } 351. switch (ls->type) { 352. case LS_OBJECT: 353. is_global = !obj_is_local((struct obj *)ls->id); 354. break; 355. case LS_MONSTER: 356. is_global = !mon_is_local((struct monst *)ls->id); 357. break; 358. default: 359. is_global = 0; 360. impossible("maybe_write_ls: bad type (%d) [range=%d]", 361. ls->type, range); 362. break; 363. } 364. /* if global and not doing local, or vice versa, count it */ 365. if (is_global ^ (range == RANGE_LEVEL)) { 366. count++; 367. if (write_it) write_ls(fd, ls); 368. } 369. } 370. 371. return count; 372. } 373.
write_ls[]
374. /* Write a light source structure to disk. */ 375. STATIC_OVL void 376. write_ls(fd, ls) 377. int fd; 378. light_source *ls; 379. { 380. genericptr_t arg_save; 381. struct obj *otmp; 382. struct monst *mtmp; 383. 384. if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { 385. if (ls->flags & LSF_NEEDS_FIXUP) 386. bwrite(fd, (genericptr_t)ls, sizeof(light_source)); 387. else { 388. /* replace object pointer with id for write, then put back */ 389. arg_save = ls->id; 390. if (ls->type == LS_OBJECT) { 391. otmp = (struct obj *)ls->id; 392. ls->id = (genericptr_t)otmp->o_id; 393. #ifdef DEBUG 394. if (find_oid((unsigned)ls->id) != otmp) 395. panic("write_ls: can't find obj #%u!", (unsigned)ls->id); 396. #endif 397. } else { /* ls->type == LS_MONSTER */ 398. mtmp = (struct monst *)ls->id; 399. ls->id = (genericptr_t)mtmp->m_id; 400. #ifdef DEBUG 401. if (find_mid((unsigned)ls->id, FM_EVERYWHERE) != mtmp) 402. panic("write_ls: can't find mon #%u!", (unsigned)ls->id); 403. #endif 404. } 405. ls->flags |= LSF_NEEDS_FIXUP; 406. bwrite(fd, (genericptr_t)ls, sizeof(light_source)); 407. ls->id = arg_save; 408. ls->flags &= ~LSF_NEEDS_FIXUP; 409. } 410. } else { 411. impossible("write_ls: bad type (%d)", ls->type); 412. } 413. } 414.
obj_move_light_source[]
415. /* Change light source's ID from src to dest. */ 416. void 417. obj_move_light_source(src, dest) 418. struct obj *src, *dest; 419. { 420. light_source *ls; 421. 422. for (ls = light_base; ls; ls = ls->next) 423. if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src) 424. ls->id = (genericptr_t) dest; 425. src->lamplit = 0; 426. dest->lamplit = 1; 427. } 428.
any_light_source[]
429. /* return true if there exist any light sources */ 430. boolean 431. any_light_source() 432. { 433. return light_base != (light_source *) 0; 434. } 435.
snuff_light_source[]
436. /* 437. * Snuff an object light source if at (x,y). This currently works 438. * only for burning light sources. 439. */ 440. void 441. snuff_light_source(x, y) 442. int x, y; 443. { 444. light_source *ls; 445. struct obj *obj; 446. 447. for (ls = light_base; ls; ls = ls->next) 448. /* 449. Is this position check valid??? Can I assume that the positions 450. will always be correct because the objects would have been 451. updated with the last vision update? [Is that recent enough???] 452. */ 453. if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) { 454. obj = (struct obj *) ls->id; 455. if (obj_is_burning(obj)) { 456. /* The only way to snuff Sunsword is to unwield it. Darkness 457. * scrolls won't affect it. (If we got here because it was 458. * dropped or thrown inside a monster, this won't matter anyway 459. * because it will go out when dropped.) 460. */ 461. if (artifact_light(obj)) continue; 462. end_burn(obj, obj->otyp != MAGIC_LAMP); 463. /* 464. * The current ls element has just been removed (and 465. * ls->next is now invalid). Return assuming that there 466. * is only one light source attached to each object. 467. */ 468. return; 469. } 470. } 471. } 472.
obj_sheds_light[]
473. /* Return TRUE if object sheds any light at all. */ 474. boolean 475. obj_sheds_light(obj) 476. struct obj *obj; 477. { 478. /* so far, only burning objects shed light */ 479. return obj_is_burning(obj); 480. } 481.
obj_is_burning[]
482. /* Return TRUE if sheds light AND will be snuffed by end_burn(). */ 483. boolean 484. obj_is_burning(obj) 485. struct obj *obj; 486. { 487. return (obj->lamplit && 488. (obj->otyp == MAGIC_LAMP || ignitable(obj) || artifact_light(obj))); 489. } 490.
obj_split_light_source[]
491. /* copy the light source(s) attachted to src, and attach it/them to dest */ 492. void 493. obj_split_light_source(src, dest) 494. struct obj *src, *dest; 495. { 496. light_source *ls, *new_ls; 497. 498. for (ls = light_base; ls; ls = ls->next) 499. if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src) { 500. /* 501. * Insert the new source at beginning of list. This will 502. * never interfere us walking down the list - we are already 503. * past the insertion point. 504. */ 505. new_ls = (light_source *) alloc(sizeof(light_source)); 506. *new_ls = *ls; 507. if (Is_candle(src)) { 508. /* split candles may emit less light than original group */ 509. ls->range = candle_light_range(src); 510. new_ls->range = candle_light_range(dest); 511. vision_full_recalc = 1; /* in case range changed */ 512. } 513. new_ls->id = (genericptr_t) dest; 514. new_ls->next = light_base; 515. light_base = new_ls; 516. dest->lamplit = 1; /* now an active light source */ 517. } 518. } 519.
obj_merge_light_sources[]
520. /* light source `src' has been folded into light source `dest'; 521. used for merging lit candles and adding candle(s) to lit candelabrum */ 522. void 523. obj_merge_light_sources(src, dest) 524. struct obj *src, *dest; 525. { 526. light_source *ls; 527. 528. /* src == dest implies adding to candelabrum */ 529. if (src != dest) end_burn(src, TRUE); /* extinguish candles */ 530. 531. for (ls = light_base; ls; ls = ls->next) 532. if (ls->type == LS_OBJECT && ls->id == (genericptr_t) dest) { 533. ls->range = candle_light_range(dest); 534. vision_full_recalc = 1; /* in case range changed */ 535. break; 536. } 537. } 538.
candle_light_range[]
539. /* Candlelight is proportional to the number of candles; 540. minimum range is 2 rather than 1 for playability. */ 541. int 542. candle_light_range(obj) 543. struct obj *obj; 544. { 545. int radius; 546. 547. if (obj->otyp == CANDELABRUM_OF_INVOCATION) { 548. /* 549. * The special candelabrum emits more light than the 550. * corresponding number of candles would. 551. * 1..3 candles, range 2 (minimum range); 552. * 4..6 candles, range 3 (normal lamp range); 553. * 7 candles, range 4 (bright). 554. */ 555. radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4; 556. } else if (Is_candle(obj)) { 557. /* 558. * Range is incremented by powers of 7 so that it will take 559. * wizard mode quantities of candles to get more light than 560. * from a lamp, without imposing an arbitrary limit. 561. * 1..6 candles, range 2; 562. * 7..48 candles, range 3; 563. * 49..342 candles, range 4; &c. 564. */ 565. long n = obj->quan; 566. 567. radius = 1; /* always incremented at least once */ 568. do { 569. radius++; 570. n /= 7L; 571. } while (n > 0L); 572. } else { 573. /* we're only called for lit candelabrum or candles */ 574. /* impossible("candlelight for %d?", obj->otyp); */ 575. radius = 3; /* lamp's value */ 576. } 577. return radius; 578. } 579.
wiz_light_sources[]
580. #ifdef WIZARD 581. extern char *FDECL(fmt_ptr, (const genericptr, char *)); /* from alloc.c */ 582. 583. int 584. wiz_light_sources() 585. { 586. winid win; 587. char buf[BUFSZ], arg_address[20]; 588. light_source *ls; 589. 590. win = create_nhwindow(NHW_MENU); /* corner text window */ 591. if (win == WIN_ERR) return 0; 592. 593. Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy); 594. putstr(win, 0, buf); 595. putstr(win, 0, ""); 596. 597. if (light_base) { 598. putstr(win, 0, "location range flags type id"); 599. putstr(win, 0, "-------- ----- ------ ---- -------"); 600. for (ls = light_base; ls; ls = ls->next) { 601. Sprintf(buf, " %2d,%2d %2d 0x%04x %s %s", 602. ls->x, ls->y, ls->range, ls->flags, 603. (ls->type == LS_OBJECT ? "obj" : 604. ls->type == LS_MONSTER ? 605. (mon_is_local((struct monst *)ls->id) ? "mon" : 606. ((struct monst *)ls->id == &youmonst) ? "you" : 607. "<m>") : /* migrating monster */ 608. "???"), 609. fmt_ptr(ls->id, arg_address)); 610. putstr(win, 0, buf); 611. } 612. } else 613. putstr(win, 0, "<none>"); 614. 615. 616. display_nhwindow(win, FALSE); 617. destroy_nhwindow(win); 618. 619. return 0; 620. } 621. 622. #endif /* WIZARD */ 623. 624. #endif /* OVL3 */ 625. 626. /*light.c*/